diff --git a/.gitignore b/.gitignore
index ee64372..9ec1673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
.idea/
*.pyc
+venv/
+v-data/
+database.sqlite3
diff --git a/README.md b/README.md
index 44ef0f8..d2efeff 100644
--- a/README.md
+++ b/README.md
@@ -2,31 +2,38 @@
Cog Function
-| Name | Status | Description
-| --- | --- | --- |
-| ccrole | **Beta** | Create custom commands that also assign roles |
-| chatter | **Alpha** | Chat-bot trained to talk like your guild
-| fight | **Incomplete** | Organize bracket tournaments within discord |
-| flag | **Beta** | Create temporary marks on users that expire after specified time |
-| hangman | **Incomplete** | Play a game of hangman |
-| immortal | **Private** | Cog designed for a specific server, not recommended to install |
-| leaver | **Incomplete** | Send a message in a channel when a user leaves the server |
-| reactrestrict | **Alpha** | Removes reactions by role per channel |
-| stealemoji | **Alpha** | Steals any custom emoji it sees |
-| werewolf | **Incomplete** | Play the classic party game Werewolf within discord |
+| Name | Status | Description (Click to see full status)
+| --- | --- | --- |
+| announcedaily | **Alpha** | Send daily announcements to all servers at a specified times
Commissioned release, so suggestions will not be accepted |
+| audiotrivia | **Alpha** | Guess the audio using the core trivia cog
Replaces the core Trivia cog. Needs help adding audio trivia lists, please submit a PR to contribute |
+| ccrole | **Beta** | Create custom commands that also assign roles
May have some bugs, please create an issue if you find any |
+| chatter | **Alpha** | Chat-bot trained to talk like your guild
Missing some key features, but currently functional |
+| coglint | **Alpha** | Error check code in python syntax posted to discord
Works, but probably needs more turning to work for cogs |
+| exclusiverole | **Alpha** | Prevent certain roles from getting any other roles
Fully functional, but pretty simple |
+| fight | **Incomplete** | Organize bracket tournaments within discord
Still in-progress, a massive project |
+| flag | **Alpha** | Create temporary marks on users that expire after specified time
Ported, will not import old data. Please report bugs |
+| forcemention | **Alpha** | Mentions unmentionable roles
Very simple cog, mention doesn't persist |
+| hangman | **Alpha** | Play a game of hangman
Some visual glitches and needs more customization |
+| howdoi | **Incomplete** | Ask coding questions and get results from StackExchange
Not yet functional |
+| leaver | **Alpha** | Send a message in a channel when a user leaves the server
Just released, please report bugs |
+| lovecalculator | **Alpha** | Calculate the love between two users
[Snap-Ons] Just updated to V3 |
+| lseen | **Alpha** | Track when a member was last online
Alpha release, please report bugs |
+| nudity | **Incomplete** | Checks for NSFW images posted in non-NSFW channels
Library this is based on has a bug, waiting for author to merge my PR |
+| planttycoon | **Alpha** | Grow your own plants!
[Snap-Ons] Updated to V3, likely to contain bugs |
+| qrinvite | **Alpha** | Create a QR code invite for the server
Alpha release, please report any bugs |
+| reactrestrict | **Alpha** | Removes reactions by role per channel
A bit clunky, but functional |
+| recyclingplant | **Alpha** | Work at a recycling plant
[Snap-Ons] Just updated to V3 |
+| rpsls | **Alpha** | Play Rock-Paper-Scissors-Lizard-Spock
[Snap-Ons] Just updated to V3 |
+| sayurl | **Alpha** | Convert any URL into text and post to discord
No error checking and pretty spammy |
+| scp | **Alpha** | Look-up SCP articles
[Snap-Ons] Just updated to V3 |
+| secrethitler | **Incomplete** | Play the Secret Hitler game
Concept, no work done yet |
+| stealemoji | **Alpha** | Steals any custom emoji it sees in a reaction
Some planned upgrades for server generation |
+| timerole | **Alpha** | Add roles to members after specified time on the server
Upgraded from V2, please report any bugs |
+| tts | **Beta** | Send a Text-to-Speech message as an uploaded mp3
Alpha release, please report any bugs |
+| unicode | **Alpha** | Encode and Decode unicode characters
[Snap-Ons] Just updated to V3 |
+| werewolf | **Pre-Alpha** | Play the classic party game Werewolf within discord
Another massive project currently being developed, will be fully customizable |
-Cog Status Descriptions
- - ccrole: May have some bugs, please create an issue if you find any
- - chatter: Missing some key features, but currently functional
- - fight: Still in-progress, a massive project
- - flag: Not yet ported to v3
- - hangman: Not yet ported to v3
- - immortal: Designed for a specific server, not recommended to install
- - leaver: Not yet ported to v3
- - reactrestrict: A bit clunky, but functional
- - stealemoji: Some planned upgrades for server generation
- - werewolf: Another massive project, will be fully customizable
+Check out my V2 cogs at [Fox-Cogs v2](https://github.com/bobloy/Fox-Cogs)
-Many of these are functional in my V2 cogs at [Fox-Cogs v2](https://github.com/bobloy/Fox-Cogs)
-
\ No newline at end of file
+Get support on the [Third Party Cog Server](https://discord.gg/GET4DVk)
diff --git a/announcedaily/__init__.py b/announcedaily/__init__.py
new file mode 100644
index 0000000..8cc69d5
--- /dev/null
+++ b/announcedaily/__init__.py
@@ -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())
diff --git a/announcedaily/announcedaily.py b/announcedaily/announcedaily.py
new file mode 100644
index 0000000..a3ad748
--- /dev/null
+++ b/announcedaily/announcedaily.py
@@ -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 ` 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 - 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 - S
diff --git a/announcedaily/info..json b/announcedaily/info..json
new file mode 100644
index 0000000..a0eb7ae
--- /dev/null
+++ b/announcedaily/info..json
@@ -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"
+ ]
+}
\ No newline at end of file
diff --git a/audiotrivia/__init__.py b/audiotrivia/__init__.py
new file mode 100644
index 0000000..6cb34ed
--- /dev/null
+++ b/audiotrivia/__init__.py
@@ -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))
diff --git a/audiotrivia/audiosession.py b/audiotrivia/audiosession.py
new file mode 100644
index 0000000..780d4b9
--- /dev/null
+++ b/audiotrivia/audiosession.py
@@ -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()
diff --git a/audiotrivia/audiotrivia.py b/audiotrivia/audiotrivia.py
new file mode 100644
index 0000000..f1d325f
--- /dev/null
+++ b/audiotrivia/audiotrivia.py
@@ -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"))
diff --git a/audiotrivia/data/lists/csgo.yaml b/audiotrivia/data/lists/csgo.yaml
new file mode 100644
index 0000000..d29b37c
--- /dev/null
+++ b/audiotrivia/data/lists/csgo.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
diff --git a/audiotrivia/data/lists/games-plab.yaml b/audiotrivia/data/lists/games-plab.yaml
new file mode 100644
index 0000000..c3a9078
--- /dev/null
+++ b/audiotrivia/data/lists/games-plab.yaml
@@ -0,0 +1,5183 @@
+AUTHOR: Plab
+https://www.youtube.com/watch?v=--bWm9hhoZo:
+- Transistor
+https://www.youtube.com/watch?v=-4nCbgayZNE:
+- Dark Cloud 2
+- Dark Cloud II
+https://www.youtube.com/watch?v=-64NlME4lJU:
+- Mega Man 7
+- Mega Man VII
+https://www.youtube.com/watch?v=-AesqnudNuw:
+- Mega Man 9
+- Mega Man IX
+https://www.youtube.com/watch?v=-BmGDtP2t7M:
+- Castlevania Curse of Darkness
+https://www.youtube.com/watch?v=-Gg6v-GMnsU:
+- 'FTL: Faster Than Light'
+https://www.youtube.com/watch?v=-GouzQ8y5Cc:
+- Minecraft
+https://www.youtube.com/watch?v=-IsFD_jw6lM:
+- Advance Wars DS
+https://www.youtube.com/watch?v=-J55bt2b3Z8:
+- Mario Kart 8
+- Mario Kart VIII
+https://www.youtube.com/watch?v=-KXPZ81aUPY:
+- 'Ratchet & Clank: Going Commando'
+- 'ratchet and clank: going commando'
+https://www.youtube.com/watch?v=-L45Lm02jIU:
+- Super Street Fighter II
+- Super Street Fighter 2
+https://www.youtube.com/watch?v=-LId8l6Rc6Y:
+- Xenosaga III
+- Xenosaga 3
+https://www.youtube.com/watch?v=-LLr-88UG1U:
+- The Last Story
+https://www.youtube.com/watch?v=-PQ9hQLWNCM:
+- Pokemon
+https://www.youtube.com/watch?v=-Q-S4wQOcr8:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=-Q2Srm60GLg:
+- Dustforce
+https://www.youtube.com/watch?v=-ROXEo0YD10:
+- Halo
+https://www.youtube.com/watch?v=-TG5VLGPdRc:
+- Wild Arms Alter Code F
+https://www.youtube.com/watch?v=-UkyW5eHKlg:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=-VtNcqxyNnA:
+- Dragon Quest II
+- Dragon Quest 2
+https://www.youtube.com/watch?v=-WQGbuqnVlc:
+- Guacamelee!
+https://www.youtube.com/watch?v=-XTYsUzDWEM:
+- 'The Legend of Zelda: Link''s Awakening'
+- 'link''s awakening'
+https://www.youtube.com/watch?v=-YfpDN84qls:
+- Bioshock Infinite
+https://www.youtube.com/watch?v=-_51UVCkOh4:
+- 'Donkey Kong Country: Tropical Freeze'
+https://www.youtube.com/watch?v=-czsPXU_Sn0:
+- StarFighter 3000
+- StarFighter MMM
+https://www.youtube.com/watch?v=-eHjgg4cnjo:
+- Tintin in Tibet
+https://www.youtube.com/watch?v=-ehGFSkPfko:
+- Contact
+https://www.youtube.com/watch?v=-finZK4D6NA:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=-ltGbYCYr-Q:
+- Enchanted Arms
+https://www.youtube.com/watch?v=-lz8ydvkFuo:
+- Super Metroid
+https://www.youtube.com/watch?v=-m3VGoy-4Qo:
+- Mega Man X6
+https://www.youtube.com/watch?v=-nOJ6c1umMU:
+- Mega Man 7
+- Mega Man VII
+https://www.youtube.com/watch?v=-oGZIqeeTt0:
+- NeoTokyo
+https://www.youtube.com/watch?v=-ohvCzPIBvM:
+- Dark Cloud
+https://www.youtube.com/watch?v=-uJOYd76nSQ:
+- Suikoden III
+- Suikoden 3
+https://www.youtube.com/watch?v=-u_udSjbXgs:
+- Radiata Stories
+https://www.youtube.com/watch?v=-xpUOrwVMHo:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=00mLin2YU54:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=01n8imWdT6g:
+- Ristar
+https://www.youtube.com/watch?v=02VD6G-JD4w:
+- SimCity 4
+- SimCity IV
+https://www.youtube.com/watch?v=03L56CE7QWc:
+- Little Nightmares
+https://www.youtube.com/watch?v=04TLq1cKeTI:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=07EXFbZaXiM:
+- Airport Tycoon 3
+- Airport Tycoon III
+https://www.youtube.com/watch?v=096M0eZMk5Q:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=0E-_TG7vGP0:
+- Battletoads & Double Dragon
+- battletoads
+- double dragon
+https://www.youtube.com/watch?v=0EhiDgp8Drg:
+- Mario Party
+https://www.youtube.com/watch?v=0F-hJjD3XAs:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=0F0ONH92OoY:
+- Donkey Kong 64
+https://www.youtube.com/watch?v=0Fbgho32K4A:
+- 'FFCC: The Crystal Bearers'
+https://www.youtube.com/watch?v=0Fff6en_Crc:
+- 'Emperor: Battle for Dune'
+https://www.youtube.com/watch?v=0H5YoFv09uQ:
+- Waterworld
+https://www.youtube.com/watch?v=0IcVJVcjAxA:
+- 'Star Ocean 3: Till the End of Time'
+- 'Star Ocean III: Till the End of Time'
+https://www.youtube.com/watch?v=0KDjcSaMgfI:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=0Lo8Q5tL0WQ:
+- Chrono Trigger
+https://www.youtube.com/watch?v=0OMlZPg8tl4:
+- Robocop 3 (C64)
+- Robocop III
+- robocop 3
+https://www.youtube.com/watch?v=0RKF6gqCXiM:
+- Persona 3
+- Persona III
+https://www.youtube.com/watch?v=0U_7HnAvbR8:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=0Y0RwyI8j8k:
+- Jet Force Gemini
+https://www.youtube.com/watch?v=0Y1Y3buSm2I:
+- Mario Kart Wii
+https://www.youtube.com/watch?v=0YN7-nirAbk:
+- Ys II Chronicles
+- Ys 2 Chronicles
+https://www.youtube.com/watch?v=0_8CS1mrfFA:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=0_YB2lagalY:
+- Marble Madness
+https://www.youtube.com/watch?v=0_ph5htjyl0:
+- Dear Esther
+https://www.youtube.com/watch?v=0dEc-UyQf58:
+- Pokemon Trading Card Game
+https://www.youtube.com/watch?v=0dMkx7c-uNM:
+- VVVVVV
+https://www.youtube.com/watch?v=0mmvYvsN32Q:
+- Batman
+https://www.youtube.com/watch?v=0oBT5dOZPig:
+- Final Fantasy X-2
+- Final Fantasy 10-2
+- Final Fantasy X-II
+https://www.youtube.com/watch?v=0ptVf0dQ18M:
+- F-Zero GX
+https://www.youtube.com/watch?v=0rz-SlHMtkE:
+- Panzer Dragoon
+https://www.youtube.com/watch?v=0tWIVmHNDYk:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=0w-9yZBE_nQ:
+- Blaster Master
+https://www.youtube.com/watch?v=0yKsce_NsWA:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=11CqmhtBfJI:
+- Wild Arms
+https://www.youtube.com/watch?v=13Uk8RB6kzQ:
+- The Smurfs
+https://www.youtube.com/watch?v=14-tduXVhNQ:
+- The Magical Quest starring Mickey Mouse
+https://www.youtube.com/watch?v=14gpqf8JP-Y:
+- Super Turrican
+https://www.youtube.com/watch?v=15PEwOkJ5DA:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=16e2Okdc-PQ:
+- Threads of Fate
+https://www.youtube.com/watch?v=16sK7JwZ9nI:
+- Sands of Destruction
+https://www.youtube.com/watch?v=18NQOEU2jvU:
+- Final Fantasy Tactics
+https://www.youtube.com/watch?v=1ALDFlUYdcQ:
+- Mega Man 10
+https://www.youtube.com/watch?v=1B0D2_zhYyM:
+- Return All Robots!
+https://www.youtube.com/watch?v=1BcHKsDr5CM:
+- Grandia
+https://www.youtube.com/watch?v=1CJ69ZxnVOs:
+- Jets 'N' Guns
+https://www.youtube.com/watch?v=1DAD230gipE:
+- 'Vampire The Masquerade: Bloodlines'
+https://www.youtube.com/watch?v=1DFIYMTnlzo:
+- 'Castlevania: Dawn of Sorrow'
+https://www.youtube.com/watch?v=1DwQk4-Smn0:
+- Crusader of Centy
+https://www.youtube.com/watch?v=1EJ2gbCFpGM:
+- Final Fantasy Adventure
+https://www.youtube.com/watch?v=1HOQJZiKbew:
+- Kirby's Adventure
+https://www.youtube.com/watch?v=1IMUSeMsxwI:
+- Touch My Katamari
+https://www.youtube.com/watch?v=1KCcXn5xBeY:
+- Inside
+https://www.youtube.com/watch?v=1KaAALej7BY:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=1MRrLo4awBI:
+- Golden Sun
+https://www.youtube.com/watch?v=1MVAIf-leiQ:
+- Fez
+https://www.youtube.com/watch?v=1NmzdFvqzSU:
+- Ruin Arm
+https://www.youtube.com/watch?v=1OzPeu60ZvU:
+- Swiv
+https://www.youtube.com/watch?v=1QHyvhnEe2g:
+- Ice Hockey
+https://www.youtube.com/watch?v=1R1-hXIeiKI:
+- Punch-Out!!
+https://www.youtube.com/watch?v=1RBvXjg_QAc:
+- 'E.T.: Return to the Green Planet'
+https://www.youtube.com/watch?v=1THa11egbMI:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=1UZ1fKOlZC0:
+- Axiom Verge
+https://www.youtube.com/watch?v=1UzoyIwC3Lg:
+- Master Spy
+https://www.youtube.com/watch?v=1X5Y6Opw26s:
+- 'The Legend of Zelda: Skyward Sword'
+- 'skyward sword'
+https://www.youtube.com/watch?v=1YWdyLlEu5w:
+- Final Fantasy Adventure
+https://www.youtube.com/watch?v=1_8u5eDjEwY:
+- 'Digital: A Love Story'
+https://www.youtube.com/watch?v=1agK890YmvQ:
+- Sonic Colors
+https://www.youtube.com/watch?v=1gZaH16gqgk:
+- Sonic Colors
+https://www.youtube.com/watch?v=1hxkqsEz4dk:
+- Mega Man Battle Network 3
+- Mega Man Battle Network III
+https://www.youtube.com/watch?v=1iKxdUnF0Vg:
+- The Revenge of Shinobi
+https://www.youtube.com/watch?v=1kt-H7qUr58:
+- Deus Ex
+https://www.youtube.com/watch?v=1r5BYjZdAtI:
+- Rusty
+https://www.youtube.com/watch?v=1riMeMvabu0:
+- Grim Fandango
+https://www.youtube.com/watch?v=1s7lAVqC0Ps:
+- Tales of Graces
+https://www.youtube.com/watch?v=1saL4_XcwVA:
+- Dustforce
+https://www.youtube.com/watch?v=1uEHUSinwD8:
+- Secret of Mana
+https://www.youtube.com/watch?v=1wskjjST4F8:
+- Plok
+https://www.youtube.com/watch?v=1xzf_Ey5th8:
+- Breath of Fire V
+- Breath of Fire 5
+https://www.youtube.com/watch?v=1yBlUvZ-taY:
+- Tribes Ascend
+https://www.youtube.com/watch?v=1zU2agExFiE:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=20Or4fIOgBk:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=248TO66gf8M:
+- Sonic Mania
+https://www.youtube.com/watch?v=29dJ6XlsMds:
+- Elvandia Story
+https://www.youtube.com/watch?v=29h1H6neu3k:
+- Dragon Quest VII
+- Dragon Quest 7
+https://www.youtube.com/watch?v=2AzKwVALPJU:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=2BNMm9irLTw:
+- 'Mario & Luigi: Superstar Saga'
+- 'mario and luigi: superstar saga'
+https://www.youtube.com/watch?v=2CEZdt5n5JQ:
+- Metal Gear Rising
+https://www.youtube.com/watch?v=2CyFFMCC67U:
+- Super Mario Land 2
+- Super Mario Land II
+https://www.youtube.com/watch?v=2Mf0f91AfQo:
+- Octopath Traveler
+https://www.youtube.com/watch?v=2MzcVSPUJw0:
+- Super Metroid
+https://www.youtube.com/watch?v=2NGWhKhMojQ:
+- Klonoa
+https://www.youtube.com/watch?v=2NfhrT3gQdY:
+- 'Lunar: The Silver Star'
+https://www.youtube.com/watch?v=2O4aNHy2Dcc:
+- X-Men vs Street Fighter
+https://www.youtube.com/watch?v=2TgWzuTOXN8:
+- Shadow of the Ninja
+https://www.youtube.com/watch?v=2WYI83Cx9Ko:
+- Earthbound
+https://www.youtube.com/watch?v=2ZX41kMN9V8:
+- 'Castlevania: Aria of Sorrow'
+https://www.youtube.com/watch?v=2bd4NId7GiA:
+- Catherine
+https://www.youtube.com/watch?v=2biS2NM9QeE:
+- Napple Tale
+https://www.youtube.com/watch?v=2c1e5ASpwjk:
+- Persona 4
+- Persona IV
+https://www.youtube.com/watch?v=2e9MvGGtz6c:
+- Secret of Mana (2018)
+- Secret of Mana
+https://www.youtube.com/watch?v=2gKlqJXIDVQ:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=2hfgF1RoqJo:
+- Gunstar Heroes
+https://www.youtube.com/watch?v=2jbJSftFr20:
+- Plok
+https://www.youtube.com/watch?v=2mLCr2sV3rs:
+- Mother
+https://www.youtube.com/watch?v=2mlPgPBDovw:
+- 'Castlevania: Bloodlines'
+https://www.youtube.com/watch?v=2oo0qQ79uWE:
+- Sonic 3D Blast (Saturn)
+- sonic 3d blast
+https://www.youtube.com/watch?v=2qDajCHTkWc:
+- 'Arc the Lad IV: Twilight of the Spirits'
+- 'Arc the Lad 4: Twilight of the Spirits'
+https://www.youtube.com/watch?v=2r1iesThvYg:
+- Chrono Trigger
+https://www.youtube.com/watch?v=2r35JpoRCOk:
+- NieR
+https://www.youtube.com/watch?v=2xP0dFTlxdo:
+- Vagrant Story
+https://www.youtube.com/watch?v=31NHdGB1ZSk:
+- Metroid Prime 3
+- Metroid Prime III
+https://www.youtube.com/watch?v=3283ANpvPPM:
+- Super Spy Hunter
+https://www.youtube.com/watch?v=37rVPijCrCA:
+- Earthbound
+https://www.youtube.com/watch?v=3Bl0nIoCB5Q:
+- Wii Sports
+https://www.youtube.com/watch?v=3Hf0L8oddrA:
+- Lagoon
+https://www.youtube.com/watch?v=3J9-q-cv_Io:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=3ODKKILZiYY:
+- Plok
+https://www.youtube.com/watch?v=3PAHwO_GsrQ:
+- Chrono Trigger
+https://www.youtube.com/watch?v=3TjzvAGDubE:
+- Super Mario 64
+https://www.youtube.com/watch?v=3WVqKTCx7Ug:
+- Wild Arms 3
+- Wild Arms III
+https://www.youtube.com/watch?v=3XM9eiSys98:
+- Hotline Miami
+https://www.youtube.com/watch?v=3Y8toBvkRpc:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=3bNlWGyxkCU:
+- Diablo III
+- Diablo 3
+https://www.youtube.com/watch?v=3cIi2PEagmg:
+- Super Mario Bros 3
+- Super Mario Bros III
+https://www.youtube.com/watch?v=3iygPesmC-U:
+- Shadow Hearts
+https://www.youtube.com/watch?v=3kmwqOIeego:
+- Touch My Katamari
+https://www.youtube.com/watch?v=3lehXRPWyes:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=3lhiseKpDDY:
+- Heimdall 2
+- Heimdall II
+https://www.youtube.com/watch?v=3nLtMX4Y8XI:
+- Mr. Nutz
+https://www.youtube.com/watch?v=3nPLwrTvII0:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=3q_o-86lmcg:
+- Crash Bandicoot
+https://www.youtube.com/watch?v=3tItkV0GeoY:
+- Diddy Kong Racing
+https://www.youtube.com/watch?v=3tpO54VR6Oo:
+- Pop'n Music 4
+- Pop'n Music IV
+https://www.youtube.com/watch?v=3vYAXZs8pFU:
+- Umineko no Naku Koro ni
+https://www.youtube.com/watch?v=44lJD2Xd5rc:
+- Super Mario World
+https://www.youtube.com/watch?v=44u87NnaV4Q:
+- 'Castlevania: Dracula X'
+https://www.youtube.com/watch?v=44vPlW8_X3s:
+- DOOM
+https://www.youtube.com/watch?v=46WQk6Qvne8:
+- Blast Corps
+https://www.youtube.com/watch?v=473L99I88n8:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=476siHQiW0k:
+- 'JESUS: Kyoufu no Bio Monster'
+https://www.youtube.com/watch?v=49rleD-HNCk:
+- Tekken Tag Tournament 2
+- Tekken Tag Tournament II
+https://www.youtube.com/watch?v=4CEc0t0t46s:
+- Ittle Dew 2
+- Ittle Dew II
+https://www.youtube.com/watch?v=4DmNrnj6wUI:
+- Napple Tale
+https://www.youtube.com/watch?v=4EBNeFI0QW4:
+- 'OFF'
+https://www.youtube.com/watch?v=4GTm-jHQm90:
+- 'Pokemon XD: Gale of Darkness'
+https://www.youtube.com/watch?v=4HHlWg5iZDs:
+- Terraria
+https://www.youtube.com/watch?v=4HLSGn4_3WE:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=4H_0h3n6pMk:
+- Silver Surfer
+https://www.youtube.com/watch?v=4J99hnghz4Y:
+- Beyond Good & Evil
+- beyond good and evil
+https://www.youtube.com/watch?v=4JJEaVI3JRs:
+- 'The Legend of Zelda: Oracle of Seasons & Ages'
+- 'oracle of seasons'
+- 'oracle of ages'
+https://www.youtube.com/watch?v=4JzDb3PZGEg:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=4Jzh0BThaaU:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=4MnzJjEuOUs:
+- 'Atelier Iris 3: Grand Phantasm'
+- 'Atelier Iris III: Grand Phantasm'
+https://www.youtube.com/watch?v=4RPbxVl6z5I:
+- Ms. Pac-Man Maze Madness
+https://www.youtube.com/watch?v=4Rh6wmLE8FU:
+- Kingdom Hearts II
+- Kingdom Hearts 2
+https://www.youtube.com/watch?v=4Ze5BfLk0J8:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=4a767iv9VaI:
+- 'Spyro: Year of the Dragon'
+https://www.youtube.com/watch?v=4axwWk4dfe8:
+- Battletoads & Double Dragon
+- battletoads
+- double dragon
+https://www.youtube.com/watch?v=4d2Wwxbsk64:
+- Dragon Ball Z Butouden 3
+- Dragon Ball Z Butouden III
+https://www.youtube.com/watch?v=4f6siAA3C9M:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=4fMd_2XeXLA:
+- 'Castlevania: Dawn of Sorrow'
+https://www.youtube.com/watch?v=4h5FzzXKjLA:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=4hWT8nYhvN0:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=4i-qGSwyu5M:
+- Breath of Fire II
+- Breath of Fire 2
+https://www.youtube.com/watch?v=4uJBIxKB01E:
+- Vay
+https://www.youtube.com/watch?v=4ugpeNkSyMc:
+- Thunder Spirits
+https://www.youtube.com/watch?v=5-0KCJvfJZE:
+- Children of Mana
+https://www.youtube.com/watch?v=52f2DQl8eGE:
+- Mega Man & Bass
+- mega man and bass
+https://www.youtube.com/watch?v=554IOtmsavA:
+- Dark Void
+https://www.youtube.com/watch?v=56oPoX8sCcY:
+- 'The Legend of Zelda: Twilight Princess'
+- 'twilight princess'
+https://www.youtube.com/watch?v=57aCSLmg9hA:
+- The Green Lantern
+https://www.youtube.com/watch?v=5B46aBeR4zo:
+- MapleStory
+https://www.youtube.com/watch?v=5CLpmBIb4MM:
+- Summoner
+https://www.youtube.com/watch?v=5Em0e5SdYs0:
+- 'Mario & Luigi: Partners in Time'
+- 'mario and luigi: partners in time'
+https://www.youtube.com/watch?v=5FDigjKtluM:
+- NieR
+https://www.youtube.com/watch?v=5IUXyzqrZsw:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=5OLxWTdtOkU:
+- Extreme-G
+https://www.youtube.com/watch?v=5OZIrAW7aSY:
+- Way of the Samurai
+https://www.youtube.com/watch?v=5WSE5sLUTnk:
+- Ristar
+https://www.youtube.com/watch?v=5ZMI6Gu2aac:
+- Suikoden III
+- Suikoden 3
+https://www.youtube.com/watch?v=5a5EDaSasRU:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=5bTAdrq6leQ:
+- Castlevania
+https://www.youtube.com/watch?v=5gCAhdDAPHE:
+- Yoshi's Island
+https://www.youtube.com/watch?v=5hVRaTn_ogE:
+- BattleBlock Theater
+https://www.youtube.com/watch?v=5hc3R4Tso2k:
+- Shadow Hearts
+https://www.youtube.com/watch?v=5kmENsE8NHc:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=5lyXiD-OYXU:
+- Wild Arms
+https://www.youtube.com/watch?v=5maIQJ79hGM:
+- 'The Legend of Zelda: Skyward Sword'
+- 'skyward sword'
+https://www.youtube.com/watch?v=5nJSvKpqXzM:
+- Legend of Mana
+https://www.youtube.com/watch?v=5niLxq7_yN4:
+- Devil May Cry 3
+- Devil May Cry III
+https://www.youtube.com/watch?v=5oGK9YN0Ux8:
+- Shadow Hearts
+https://www.youtube.com/watch?v=5pQMJEzNwtM:
+- Street Racer
+https://www.youtube.com/watch?v=5trQZ9u9xNM:
+- Final Fantasy Tactics
+https://www.youtube.com/watch?v=5w_SgBImsGg:
+- 'The Legend of Zelda: Skyward Sword'
+- 'skyward sword'
+https://www.youtube.com/watch?v=62HoIMZ8xAE:
+- Alundra
+https://www.youtube.com/watch?v=62_S0Sl02TM:
+- Alundra 2
+- Alundra II
+https://www.youtube.com/watch?v=66-rV8v6TNo:
+- Cool World
+https://www.youtube.com/watch?v=67nrJieFVI0:
+- 'World of Warcraft: Wrath of the Lich King'
+https://www.youtube.com/watch?v=69142JeBFXM:
+- Baten Kaitos Origins
+https://www.youtube.com/watch?v=6AZLmFaSpR0:
+- 'Castlevania: Lords of Shadow'
+https://www.youtube.com/watch?v=6AuCTNAJz_M:
+- Rollercoaster Tycoon 3
+- Rollercoaster Tycoon III
+https://www.youtube.com/watch?v=6CMTXyExkeI:
+- Final Fantasy V
+- Final Fantasy 5
+https://www.youtube.com/watch?v=6FdjTwtvKCE:
+- Bit.Trip Flux
+https://www.youtube.com/watch?v=6GWxoOc3TFI:
+- Super Mario Land
+https://www.youtube.com/watch?v=6GX_qN7hEEM:
+- Faxanadu
+https://www.youtube.com/watch?v=6GhseRvdAgs:
+- Tekken 5
+- Tekken V
+https://www.youtube.com/watch?v=6IadffCqEQw:
+- The Legend of Zelda
+https://www.youtube.com/watch?v=6JdMvEBtFSE:
+- 'Parasite Eve: The 3rd Birthday'
+https://www.youtube.com/watch?v=6JuO7v84BiY:
+- Pop'n Music 16 PARTY
+- Pop'n Music XVI PARTY
+https://www.youtube.com/watch?v=6MQRL7xws7w:
+- Wild Arms
+https://www.youtube.com/watch?v=6R8jGeVw-9Y:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=6TJWqX8i8-E:
+- 'Moon: Remix RPG Adventure'
+https://www.youtube.com/watch?v=6UWGh1A1fPw:
+- Dustforce
+https://www.youtube.com/watch?v=6VD_aVMOL1c:
+- Dark Cloud 2
+- Dark Cloud II
+https://www.youtube.com/watch?v=6XOEZIZMUl0:
+- 'SMT: Digital Devil Saga 2'
+- 'SMT: Digital Devil Saga II'
+https://www.youtube.com/watch?v=6ZiYK9U4TfE:
+- Dirt Trax FX
+https://www.youtube.com/watch?v=6_JLe4OxrbA:
+- Super Mario 64
+https://www.youtube.com/watch?v=6b77tr2Vu9U:
+- Pokemon
+https://www.youtube.com/watch?v=6fA_EQBPB94:
+- Super Metroid
+https://www.youtube.com/watch?v=6jp9d66QRz0:
+- Jazz Jackrabbit 2
+- Jazz Jackrabbit II
+https://www.youtube.com/watch?v=6kIE2xVJdTc:
+- Earthbound
+https://www.youtube.com/watch?v=6l3nVyGhbpE:
+- Extreme-G
+https://www.youtube.com/watch?v=6l8a_pzRcRI:
+- Blast Corps
+https://www.youtube.com/watch?v=6nJgdcnFtcQ:
+- Snake's Revenge
+https://www.youtube.com/watch?v=6paAqMXurdA:
+- Chrono Trigger
+https://www.youtube.com/watch?v=6s6Bc0QAyxU:
+- Ghosts 'n' Goblins
+https://www.youtube.com/watch?v=6uWBacK2RxI:
+- 'Mr. Nutz: Hoppin'' Mad'
+https://www.youtube.com/watch?v=6uo5ripzKwc:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=6xXHeaLmAcM:
+- Mario + Rabbids Kingdom Battle
+https://www.youtube.com/watch?v=70oTg2go0XU:
+- 3D Dot Game Heroes
+https://www.youtube.com/watch?v=718qcWPzvAY:
+- Super Paper Mario
+https://www.youtube.com/watch?v=72RLQGHxE08:
+- Zack & Wiki
+- zack and wiki
+https://www.youtube.com/watch?v=745hAPheACw:
+- Dark Cloud 2
+- Dark Cloud II
+https://www.youtube.com/watch?v=74cYr5ZLeko:
+- Mega Man 9
+- Mega Man IX
+https://www.youtube.com/watch?v=763w2hsKUpk:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=77F1lLI5LZw:
+- Grandia
+https://www.youtube.com/watch?v=77Z3VDq_9_0:
+- Street Fighter II
+- Street Fighter 2
+https://www.youtube.com/watch?v=7DC2Qj2LKng:
+- Wild Arms
+https://www.youtube.com/watch?v=7DYL2blxWSA:
+- Gran Turismo
+https://www.youtube.com/watch?v=7Dwc0prm7z4:
+- Child of Eden
+https://www.youtube.com/watch?v=7F3KhzpImm4:
+- 'The Legend of Zelda: Twilight Princess'
+- 'twilight princess'
+https://www.youtube.com/watch?v=7FwtoHygavA:
+- Super Mario 3D Land
+https://www.youtube.com/watch?v=7HMGj_KUBzU:
+- Mega Man 6
+- Mega Man VI
+https://www.youtube.com/watch?v=7JIkz4g0dQc:
+- Mega Man 10
+https://www.youtube.com/watch?v=7JWi5dyzW2Q:
+- Dark Cloud 2
+- Dark Cloud II
+https://www.youtube.com/watch?v=7L3VJpMORxM:
+- Lost Odyssey
+https://www.youtube.com/watch?v=7MQFljss6Pc:
+- Red Dead Redemption
+https://www.youtube.com/watch?v=7MhWtz8Nv9Q:
+- The Last Remnant
+https://www.youtube.com/watch?v=7OHV_ByQIIw:
+- Plok
+https://www.youtube.com/watch?v=7RDRhAKsLHo:
+- Dragon Quest V
+- Dragon Quest 5
+https://www.youtube.com/watch?v=7X5-xwb6otQ:
+- Lady Stalker
+https://www.youtube.com/watch?v=7Y9ea3Ph7FI:
+- Unreal Tournament 2003 & 2004
+- unreal tournament 2003
+- unreal tournament 2004
+https://www.youtube.com/watch?v=7d8nmKL5hbU:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=7lHAHFl_3u0:
+- Shadow of the Colossus
+https://www.youtube.com/watch?v=7m2yOHjObCM:
+- Chrono Trigger
+https://www.youtube.com/watch?v=7rNgsqxnIuY:
+- Magic Johnson's Fast Break
+https://www.youtube.com/watch?v=7sb2q6JedKk:
+- Anodyne
+https://www.youtube.com/watch?v=7sc7R7jeOX0:
+- Sonic and the Black Knight
+https://www.youtube.com/watch?v=7u3tJbtAi_o:
+- Grounseed
+https://www.youtube.com/watch?v=7vpHPBE59HE:
+- Valkyria Chronicles
+https://www.youtube.com/watch?v=8-Q-UsqJ8iM:
+- Katamari Damacy
+https://www.youtube.com/watch?v=80YFKvaRou4:
+- Mass Effect
+https://www.youtube.com/watch?v=81-SoTxMmiI:
+- Deep Labyrinth
+https://www.youtube.com/watch?v=81dgZtXKMII:
+- 'Nintendo 3DS Guide: Louvre'
+https://www.youtube.com/watch?v=83p9uYd4peM:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=83xEzdYAmSg:
+- Oceanhorn
+https://www.youtube.com/watch?v=84NsPpkY4l0:
+- Live a Live
+https://www.youtube.com/watch?v=88VyZ4Lvd0c:
+- Wild Arms
+https://www.youtube.com/watch?v=8EaxiB6Yt5g:
+- Earthbound
+https://www.youtube.com/watch?v=8Fl6WlJ-Pms:
+- Parasite Eve
+https://www.youtube.com/watch?v=8IP_IsXL7b4:
+- Super Mario Kart
+https://www.youtube.com/watch?v=8IVlnImPqQA:
+- Unreal Tournament 2003 & 2004
+- unreal tournament 2003
+- unreal tournament 2004
+https://www.youtube.com/watch?v=8K35MD4ZNRU:
+- Kirby's Epic Yarn
+https://www.youtube.com/watch?v=8K8hCmRDbWc:
+- Blue Dragon
+https://www.youtube.com/watch?v=8KX9L6YkA78:
+- Xenosaga III
+- Xenosaga 3
+https://www.youtube.com/watch?v=8MRHV_Cf41E:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=8OFao351gwU:
+- Mega Man 3
+- Mega Man III
+https://www.youtube.com/watch?v=8RtLhXibDfA:
+- Yoshi's Island
+https://www.youtube.com/watch?v=8SJl4mELFMM:
+- 'Pokemon Mystery Dungeon: Explorers of Sky'
+https://www.youtube.com/watch?v=8ZjZXguqmKM:
+- The Smurfs
+https://www.youtube.com/watch?v=8_9Dc7USBhY:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=8ajBHjJyVDE:
+- 'The Legend of Zelda: A Link Between Worlds'
+- 'a link between worlds'
+https://www.youtube.com/watch?v=8bEtK6g4g_Y:
+- 'Dragon Ball Z: Super Butoden'
+https://www.youtube.com/watch?v=8eZRNAtq_ps:
+- 'Target: Renegade'
+https://www.youtube.com/watch?v=8gGv1TdQaMI:
+- Tomb Raider II
+- Tomb Raider 2
+https://www.youtube.com/watch?v=8hLQart9bsQ:
+- Shadowrun Returns
+https://www.youtube.com/watch?v=8hzjxWVQnxo:
+- Soma
+https://www.youtube.com/watch?v=8iBCg85y0K8:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=8kBBJQ_ySpI:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=8lmjoPgEWb4:
+- Wangan Midnight Maximum Tune 3
+- Wangan Midnight Maximum Tune III
+https://www.youtube.com/watch?v=8mcUQ8Iv6QA:
+- MapleStory
+https://www.youtube.com/watch?v=8pJ4Q7L9rBs:
+- NieR
+https://www.youtube.com/watch?v=8qNwJeuk4gY:
+- Mega Man X
+https://www.youtube.com/watch?v=8qy4-VeSnYk:
+- Secret of Mana
+https://www.youtube.com/watch?v=8tffqG3zRLQ:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=8urO2NlhdiU:
+- Halo 3 ODST
+- Halo III ODST
+https://www.youtube.com/watch?v=8x1E_1TY8ig:
+- Double Dragon Neon
+https://www.youtube.com/watch?v=8xWWLSlQPeI:
+- 'The Legend of Zelda: Wind Waker'
+- 'wind waker'
+https://www.youtube.com/watch?v=8zY_4-MBuIc:
+- 'Phoenix Wright: Ace Attorney'
+https://www.youtube.com/watch?v=93Fqrbd-9gI:
+- Opoona
+https://www.youtube.com/watch?v=93jNP6y_Az8:
+- Yoshi's New Island
+https://www.youtube.com/watch?v=96ro-5alCGo:
+- One Piece Grand Battle 2
+- One Piece Grand Battle II
+https://www.youtube.com/watch?v=99RVgsDs1W0:
+- 'The Legend of Zelda: Twilight Princess'
+- 'twilight princess'
+https://www.youtube.com/watch?v=9BF1JT8rNKI:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=9BgCJL71YIY:
+- Wild Arms 2
+- Wild Arms II
+https://www.youtube.com/watch?v=9FZ-12a3dTI:
+- Deus Ex
+https://www.youtube.com/watch?v=9GvO7CWsWEg:
+- Baten Kaitos
+https://www.youtube.com/watch?v=9QVLuksB-d8:
+- 'Pop''n Music 12: Iroha'
+- 'Pop''n Music XII: Iroha'
+https://www.youtube.com/watch?v=9VE72cRcQKk:
+- Super Runabout
+https://www.youtube.com/watch?v=9VyMkkI39xc:
+- Okami
+https://www.youtube.com/watch?v=9WlrcP2zcys:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=9YRGh-hQq5c:
+- Journey
+https://www.youtube.com/watch?v=9YY-v0pPRc8:
+- Environmental Station Alpha
+https://www.youtube.com/watch?v=9YoDuIZa8tE:
+- Lost Odyssey
+https://www.youtube.com/watch?v=9ZanHcT3wJI:
+- Contact
+https://www.youtube.com/watch?v=9_wYMNZYaA4:
+- Radiata Stories
+https://www.youtube.com/watch?v=9alsJe-gEts:
+- 'The Legend of Heroes: Trails in the Sky'
+https://www.youtube.com/watch?v=9cJe5v5lLKk:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=9heorR5vEvA:
+- Streets of Rage
+https://www.youtube.com/watch?v=9kkQaWcpRvI:
+- Secret of Mana
+https://www.youtube.com/watch?v=9oVbqhGBKac:
+- Castle in the Darkness
+https://www.youtube.com/watch?v=9rldISzBkjE:
+- Undertale
+https://www.youtube.com/watch?v=9sVb_cb7Skg:
+- Diablo II
+- Diablo 2
+https://www.youtube.com/watch?v=9sYfDXfMO0o:
+- Parasite Eve
+https://www.youtube.com/watch?v=9saTXWj78tY:
+- Katamari Damacy
+https://www.youtube.com/watch?v=9th-yImn9aA:
+- Baten Kaitos
+https://www.youtube.com/watch?v=9u3xNXai8D8:
+- Civilization IV
+- Civilization 4
+https://www.youtube.com/watch?v=9v8qNLnTxHU:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=9v_B9wv_qTw:
+- Tower of Heaven
+https://www.youtube.com/watch?v=9xy9Q-BLp48:
+- Legaia 2
+- Legaia II
+https://www.youtube.com/watch?v=A-AmRMWqcBk:
+- Stunt Race FX
+https://www.youtube.com/watch?v=A-dyJWsn_2Y:
+- Pokken Tournament
+https://www.youtube.com/watch?v=A1b4QO48AoA:
+- Hollow Knight
+https://www.youtube.com/watch?v=A2Mi7tkE5T0:
+- Secret of Mana
+https://www.youtube.com/watch?v=A3PE47hImMo:
+- Paladin's Quest II
+- Paladin's Quest 2
+https://www.youtube.com/watch?v=A4e_sQEMC8c:
+- Streets of Fury
+https://www.youtube.com/watch?v=A6Qolno2AQA:
+- Super Princess Peach
+https://www.youtube.com/watch?v=A9PXQSFWuRY:
+- Radiant Historia
+https://www.youtube.com/watch?v=ABYH2x7xaBo:
+- Faxanadu
+https://www.youtube.com/watch?v=AC58piv97eM:
+- 'The Binding of Isaac: Afterbirth'
+https://www.youtube.com/watch?v=AE0hhzASHwY:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=AGWVzDhDHMc:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=AISrz88SJNI:
+- Digital Devil Saga
+https://www.youtube.com/watch?v=AJX08k_VZv8:
+- 'Ace Combat 4: Shattered Skies'
+- 'Ace Combat IV: Shattered Skies'
+https://www.youtube.com/watch?v=AKfLOMirwho:
+- Earthbound
+https://www.youtube.com/watch?v=AN-NbukIjKw:
+- Super Mario 64
+https://www.youtube.com/watch?v=APW3ZX8FvvE:
+- 'Phoenix Wright: Ace Attorney'
+https://www.youtube.com/watch?v=AQMonx8SlXc:
+- Enthusia Professional Racing
+https://www.youtube.com/watch?v=ARQfmb30M14:
+- Bejeweled 3
+- Bejeweled III
+https://www.youtube.com/watch?v=ARTuLmKjA7g:
+- King of Fighters 2002 Unlimited Match
+https://www.youtube.com/watch?v=ASl7qClvqTE:
+- 'Roommania #203'
+- 'Roommania #CCIII'
+https://www.youtube.com/watch?v=AU_tnstiX9s:
+- 'Ecco the Dolphin: Defender of the Future'
+https://www.youtube.com/watch?v=AVvhihA9gRc:
+- Berserk
+https://www.youtube.com/watch?v=AW7oKCS8HjM:
+- Hotline Miami
+https://www.youtube.com/watch?v=AWB3tT7e3D8:
+- 'The Legend of Zelda: Spirit Tracks'
+- 'spirit tracks'
+https://www.youtube.com/watch?v=AbRWDpruNu4:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=AdI6nJ_sPKQ:
+- Super Stickman Golf 3
+- Super Stickman Golf III
+https://www.youtube.com/watch?v=Ag-O4VfJx6U:
+- Metal Gear Solid 2
+- Metal Gear Solid II
+https://www.youtube.com/watch?v=AkKMlbmq6mc:
+- Mass Effect 2
+- Mass Effect II
+https://www.youtube.com/watch?v=Al0XOLM9FPw:
+- Skullmonkeys
+https://www.youtube.com/watch?v=AmDE3fCW9gY:
+- Okamiden
+https://www.youtube.com/watch?v=AtXEw2NgXx8:
+- Final Fantasy XII
+- Final Fantasy 12
+https://www.youtube.com/watch?v=AuluLeMp1aA:
+- Majokko de Go Go
+https://www.youtube.com/watch?v=AvZjyCGUj-Q:
+- Final Fantasy Tactics A2
+https://www.youtube.com/watch?v=AvlfNZ685B8:
+- Chaos Legion
+https://www.youtube.com/watch?v=AxVhRs8QC1U:
+- Banjo-Kazooie
+https://www.youtube.com/watch?v=Az9XUAcErIQ:
+- Tomb Raider
+https://www.youtube.com/watch?v=AzlWTsBn8M8:
+- Tetris
+https://www.youtube.com/watch?v=B-L4jj9lRmE:
+- Suikoden III
+- Suikoden 3
+https://www.youtube.com/watch?v=B0nk276pUv8:
+- Shovel Knight
+https://www.youtube.com/watch?v=B1e6VdnjLuA:
+- Shadow of the Colossus
+https://www.youtube.com/watch?v=B2j3_kaReP4:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=B4JvKl7nvL0:
+- Grand Theft Auto IV
+- Grand Theft Auto 4
+https://www.youtube.com/watch?v=B8MpofvFtqY:
+- 'Mario & Luigi: Superstar Saga'
+- 'mario and luigi: superstar saga'
+https://www.youtube.com/watch?v=BCjRd3LfRkE:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=BDg0P_L57SU:
+- Lagoon
+https://www.youtube.com/watch?v=BKmv_mecn5g:
+- Super Mario Sunshine
+https://www.youtube.com/watch?v=BMpvrfwD7Hg:
+- Kirby's Epic Yarn
+https://www.youtube.com/watch?v=BQRKQ-CQ27U:
+- 3D Dot Game Heroes
+https://www.youtube.com/watch?v=BSVBfElvom8:
+- 'The Legend of Zelda: Ocarina of Time'
+- 'ocarina of time'
+https://www.youtube.com/watch?v=BVLMdQfxzo4:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=BZWiBxlBCbM:
+- Hollow Knight
+https://www.youtube.com/watch?v=B_QTkyu2Ssk:
+- Yoshi's Island
+https://www.youtube.com/watch?v=B_ed-ZF9yR0:
+- Ragnarok Online
+https://www.youtube.com/watch?v=Ba4J-4bUN0w:
+- Bomberman Hero
+https://www.youtube.com/watch?v=BdFLRkDRtP0:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=BdlkxaSEgB0:
+- Galactic Pinball
+https://www.youtube.com/watch?v=BfR5AmZcZ9g:
+- Kirby Super Star
+https://www.youtube.com/watch?v=BfVmj3QGQDw:
+- The Neverhood
+https://www.youtube.com/watch?v=BhfevIZsXo0:
+- Outlast
+https://www.youtube.com/watch?v=BimaIgvOa-A:
+- Paper Mario
+https://www.youtube.com/watch?v=Bj5bng0KRPk:
+- 'The Legend of Zelda: Ocarina of Time'
+- 'ocarina of time'
+https://www.youtube.com/watch?v=Bk_NDMKfiVE:
+- Chrono Cross
+https://www.youtube.com/watch?v=BkmbbZZOgKg:
+- Cheetahmen II
+- Cheetahmen 2
+https://www.youtube.com/watch?v=Bkmn35Okxfw:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=Bqvy5KIeQhI:
+- Legend of Grimrock II
+- Legend of Grimrock 2
+https://www.youtube.com/watch?v=BqxjLbpmUiU:
+- Krater
+https://www.youtube.com/watch?v=Bvw2H15HDlM:
+- 'Final Fantasy XI: Rise of the Zilart'
+- 'Final Fantasy 11: Rise of the Zilart'
+https://www.youtube.com/watch?v=BwpzdyCvqN0:
+- Final Fantasy Adventure
+https://www.youtube.com/watch?v=BxYfQBNFVSw:
+- Mega Man 7
+- Mega Man VII
+https://www.youtube.com/watch?v=C-QGg9FGzj4:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=C31ciPeuuXU:
+- 'Golden Sun: Dark Dawn'
+https://www.youtube.com/watch?v=C3xhG7wRnf0:
+- Super Paper Mario
+https://www.youtube.com/watch?v=C4cD-7dOohU:
+- Valdis Story
+https://www.youtube.com/watch?v=C7NTTBm7fS8:
+- Mighty Switch Force! 2
+- Mighty Switch Force! II
+https://www.youtube.com/watch?v=C7r8sJbeOJc:
+- NeoTokyo
+https://www.youtube.com/watch?v=C8aVq5yQPD8:
+- Lode Runner 3-D
+- Lode Runner 3-500
+- Lode Runner III-D
+https://www.youtube.com/watch?v=CADHl-iZ_Kw:
+- 'The Legend of Zelda: A Link to the Past'
+- 'a link to the past'
+https://www.youtube.com/watch?v=CBm1yaZOrB0:
+- Enthusia Professional Racing
+https://www.youtube.com/watch?v=CD9A7myidl4:
+- No More Heroes 2
+- No More Heroes II
+https://www.youtube.com/watch?v=CEPnbBgjWdk:
+- 'Brothers: A Tale of Two Sons'
+https://www.youtube.com/watch?v=CHQ56GSPi2I:
+- 'Arc the Lad IV: Twilight of the Spirits'
+- 'Arc the Lad 4: Twilight of the Spirits'
+https://www.youtube.com/watch?v=CHd4iWEFARM:
+- Klonoa
+https://www.youtube.com/watch?v=CHlEPgi4Fro:
+- 'Ace Combat Zero: The Belkan War'
+https://www.youtube.com/watch?v=CHydNVrPpAQ:
+- 'The Legend of Zelda: Skyward Sword'
+- 'skyward sword'
+https://www.youtube.com/watch?v=CLl8UR5vrMk:
+- Yakuza 0
+- 'Yakuza '
+https://www.youtube.com/watch?v=CNUgwyd2iIA:
+- Sonic the Hedgehog 3
+- Sonic the Hedgehog III
+https://www.youtube.com/watch?v=CP0TcAnHftc:
+- Attack of the Friday Monsters! A Tokyo Tale
+https://www.youtube.com/watch?v=CPKoMt4QKmw:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=CPbjTzqyr7o:
+- Last Bible III
+- Last Bible 3
+https://www.youtube.com/watch?v=CRmOTY1lhYs:
+- Mass Effect
+https://www.youtube.com/watch?v=CYjYCaqshjE:
+- No More Heroes 2
+- No More Heroes II
+https://www.youtube.com/watch?v=CYswIEEMIWw:
+- Sonic CD
+- Sonic 400
+https://www.youtube.com/watch?v=Ca4QJ_pDqpA:
+- 'Dragon Ball Z: The Legacy of Goku II'
+- 'Dragon Ball Z: The Legacy of Goku 2'
+https://www.youtube.com/watch?v=Car2R06WZcw:
+- 'The Legend of Zelda: Wind Waker'
+- 'wind waker'
+https://www.youtube.com/watch?v=CcKUWCm_yRs:
+- Cool Spot
+https://www.youtube.com/watch?v=CcovgBkMTmE:
+- Guild Wars 2
+- Guild Wars II
+https://www.youtube.com/watch?v=CfoiK5ADejI:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=CgtvppDEyeU:
+- Shenmue
+https://www.youtube.com/watch?v=CkPqtTcBXTA:
+- Bully
+https://www.youtube.com/watch?v=CksHdwwG1yw:
+- Crystalis
+https://www.youtube.com/watch?v=Cm9HjyPkQbg:
+- Soul Edge
+https://www.youtube.com/watch?v=CmMswzZkMYY:
+- Super Metroid
+https://www.youtube.com/watch?v=Cnn9BW3OpJs:
+- 'Roommania #203'
+- 'Roommania #CCIII'
+https://www.youtube.com/watch?v=CoQrXSc_PPw:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=Cp0UTM-IzjM:
+- 'Castlevania: Lament of Innocence'
+https://www.youtube.com/watch?v=CpThkLTJjUQ:
+- Turok 2 (Gameboy)
+- Turok II
+- turok 2
+https://www.youtube.com/watch?v=CqAXFK8U32U:
+- McKids
+https://www.youtube.com/watch?v=CrjvBd9q4A0:
+- Demon's Souls
+https://www.youtube.com/watch?v=Ct54E7GryFQ:
+- Persona 4
+- Persona IV
+https://www.youtube.com/watch?v=CuQJ-qh9s_s:
+- Tekken 2
+- Tekken II
+https://www.youtube.com/watch?v=CumPOZtsxkU:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=Cw4IHZT7guM:
+- Final Fantasy XI
+- Final Fantasy 11
+https://www.youtube.com/watch?v=CwI39pDPlgc:
+- Shadow Madness
+https://www.youtube.com/watch?v=CzZab0uEd1w:
+- 'Tintin: Prisoners of the Sun'
+https://www.youtube.com/watch?v=D0uYrKpNE2o:
+- Valkyrie Profile
+https://www.youtube.com/watch?v=D2nWuGoRU0s:
+- 'Ecco the Dolphin: Defender of the Future'
+https://www.youtube.com/watch?v=D30VHuqhXm8:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=D3zfoec6tiw:
+- NieR
+https://www.youtube.com/watch?v=DFKoFzNfQdA:
+- Secret of Mana
+https://www.youtube.com/watch?v=DIyhbwBfOwE:
+- Tekken 4
+- Tekken IV
+https://www.youtube.com/watch?v=DKbzLuiop80:
+- Shin Megami Tensei Nocturne
+https://www.youtube.com/watch?v=DLqos66n3Qo:
+- Mega Turrican
+https://www.youtube.com/watch?v=DPO2XhA5F3Q:
+- Mana Khemia
+https://www.youtube.com/watch?v=DS825tbc9Ts:
+- Phantasy Star Online
+https://www.youtube.com/watch?v=DSOvrM20tMA:
+- Wild Arms
+https://www.youtube.com/watch?v=DTqp7jUBoA8:
+- Mega Man 3
+- Mega Man III
+https://www.youtube.com/watch?v=DTzf-vahsdI:
+- 'The Legend of Zelda: Breath of the Wild'
+- 'breath of the wild'
+https://www.youtube.com/watch?v=DW-tMwk3t04:
+- Grounseed
+https://www.youtube.com/watch?v=DWXXhLbqYOI:
+- Mario Kart 64
+https://www.youtube.com/watch?v=DY0L5o9y-YA:
+- Paladin's Quest
+https://www.youtube.com/watch?v=DY_Tz7UAeAY:
+- Darksiders II
+- Darksiders 2
+https://www.youtube.com/watch?v=DZQ1P_oafJ0:
+- Eternal Champions
+https://www.youtube.com/watch?v=DZaltYb0hjU:
+- Western Lords
+https://www.youtube.com/watch?v=DbQdgOVOjSU:
+- Super Mario RPG
+https://www.youtube.com/watch?v=DeqecCzDWhQ:
+- Streets of Rage 2
+- Streets of Rage II
+https://www.youtube.com/watch?v=Dhd4jJw8VtE:
+- 'Phoenix Wright: Ace Attorney'
+https://www.youtube.com/watch?v=DjKfEQD892I:
+- To the Moon
+https://www.youtube.com/watch?v=DlbCke52EBQ:
+- 'The Legend of Zelda: Skyward Sword'
+- 'skyward sword'
+https://www.youtube.com/watch?v=DlcwDU0i6Mw:
+- Tribes 2
+- Tribes II
+https://www.youtube.com/watch?v=DmaFexLIh4s:
+- El Shaddai
+https://www.youtube.com/watch?v=DmpP-RMFNHo:
+- 'The Elder Scrolls III: Morrowind'
+- 'The Elder Scrolls 3: Morrowind'
+- Morrowind
+https://www.youtube.com/watch?v=DoQekfFkXvI:
+- Super Mario Land
+https://www.youtube.com/watch?v=Dr95nRD7vAo:
+- FTL
+https://www.youtube.com/watch?v=DxEl2p9GPnY:
+- The Lost Vikings
+https://www.youtube.com/watch?v=DzXQKut6Ih4:
+- 'Castlevania: Dracula X'
+https://www.youtube.com/watch?v=E3PKlQUYNTw:
+- Okamiden
+https://www.youtube.com/watch?v=E3Po0o6zoJY:
+- 'TrackMania 2: Stadium'
+- 'TrackMania II: Stadium'
+https://www.youtube.com/watch?v=E5K6la0Fzuw:
+- Driver
+https://www.youtube.com/watch?v=E99qxCMb05g:
+- VVVVVV
+https://www.youtube.com/watch?v=ECP710r6JCM:
+- Super Smash Bros. Wii U / 3DS
+- super smash bros wii u
+- super smash bros 3ds
+- super smash bros. wii u
+- super smash bros. 3ds
+https://www.youtube.com/watch?v=EDmsNVWZIws:
+- Dragon Quest
+https://www.youtube.com/watch?v=EF7QwcRAwac:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=EF_lbrpdRQo:
+- Contact
+https://www.youtube.com/watch?v=EHAbZoBjQEw:
+- Secret of Evermore
+https://www.youtube.com/watch?v=EHRfd2EQ_Do:
+- Persona 4
+- Persona IV
+https://www.youtube.com/watch?v=EL_jBUYPi88:
+- Journey to Silius
+https://www.youtube.com/watch?v=ELqpqweytFc:
+- Metroid Prime
+https://www.youtube.com/watch?v=ELyz549E_f4:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=ENStkWiosK4:
+- Kid Icarus
+https://www.youtube.com/watch?v=EQjT6103nLg:
+- Senko no Ronde (Wartech)
+- Wartech
+- senko no ronde
+https://www.youtube.com/watch?v=ERUnY6EIsn8:
+- Snake Pass
+https://www.youtube.com/watch?v=ERohLbXvzVQ:
+- Z-Out
+https://www.youtube.com/watch?v=EVo5O3hKVvo:
+- Mega Turrican
+https://www.youtube.com/watch?v=EX5V_PWI3yM:
+- Kingdom Hearts II
+- Kingdom Hearts 2
+https://www.youtube.com/watch?v=EcUxGQkLj2c:
+- Final Fantasy III DS
+- Final Fantasy 3 DS
+https://www.youtube.com/watch?v=EdkkgkEu_NQ:
+- The Smurfs' Nightmare
+https://www.youtube.com/watch?v=EeGhYL_8wtg:
+- Phantasy Star Portable 2
+- Phantasy Star Portable II
+https://www.youtube.com/watch?v=EeXlQNJnjj0:
+- 'E.V.O: Search for Eden'
+https://www.youtube.com/watch?v=Ef3xB2OP8l8:
+- Diddy Kong Racing
+https://www.youtube.com/watch?v=Ef5pp7mt1lA:
+- 'Animal Crossing: Wild World'
+https://www.youtube.com/watch?v=Ekiz0YMNp7A:
+- 'Romancing SaGa: Minstrel Song'
+https://www.youtube.com/watch?v=ElSUKQOF3d4:
+- Elebits
+https://www.youtube.com/watch?v=EmD9WnLYR5I:
+- Super Mario Land 2
+- Super Mario Land II
+https://www.youtube.com/watch?v=EohQnQbQQWk:
+- Super Mario Kart
+https://www.youtube.com/watch?v=EpbcztAybh0:
+- Super Mario Maker
+https://www.youtube.com/watch?v=ErlBKXnOHiQ:
+- Dragon Quest IV
+- Dragon Quest 4
+https://www.youtube.com/watch?v=Ev1MRskhUmA:
+- Dragon Quest VI
+- Dragon Quest 6
+https://www.youtube.com/watch?v=EvRTjXbb8iw:
+- Unlimited Saga
+https://www.youtube.com/watch?v=F0cuCvhbF9k:
+- 'The Legend of Zelda: Breath of the Wild'
+- 'breath of the wild'
+https://www.youtube.com/watch?v=F2-bROS64aI:
+- Mega Man Soccer
+https://www.youtube.com/watch?v=F3hz58VDWs8:
+- Chrono Trigger
+https://www.youtube.com/watch?v=F41PKROUnhA:
+- F-Zero GX
+https://www.youtube.com/watch?v=F4QbiPftlEE:
+- Dustforce
+https://www.youtube.com/watch?v=F6sjYt6EJVw:
+- Journey to Silius
+https://www.youtube.com/watch?v=F7p1agUovzs:
+- 'Silent Hill: Shattered Memories'
+https://www.youtube.com/watch?v=F8U5nxhxYf0:
+- Breath of Fire III
+- Breath of Fire 3
+https://www.youtube.com/watch?v=FCB7Dhm8RuY:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=FDAMxLKY2EY:
+- Ogre Battle
+https://www.youtube.com/watch?v=FE59rlKJRPs:
+- Rogue Galaxy
+https://www.youtube.com/watch?v=FEpAD0RQ66w:
+- Shinobi III
+- Shinobi 3
+https://www.youtube.com/watch?v=FGtk_eaVnxQ:
+- Minecraft
+https://www.youtube.com/watch?v=FJ976LQSY-k:
+- Western Lords
+https://www.youtube.com/watch?v=FJJPaBA7264:
+- 'Castlevania: Aria of Sorrow'
+https://www.youtube.com/watch?v=FKqTtZXIid4:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=FKtnlUcGVvM:
+- Star Ocean 3
+- Star Ocean III
+https://www.youtube.com/watch?v=FPjueitq904:
+- "Einh\xC3\xA4nder"
+https://www.youtube.com/watch?v=FQioui9YeiI:
+- Ragnarok Online
+https://www.youtube.com/watch?v=FR-TFI71s6w:
+- Super Monkey Ball 2
+- Super Monkey Ball II
+https://www.youtube.com/watch?v=FSfRr0LriBE:
+- Hearthstone
+https://www.youtube.com/watch?v=FWSwAKVS-IA:
+- 'Disaster: Day of Crisis'
+https://www.youtube.com/watch?v=FYSt4qX85oA:
+- 'Star Ocean 3: Till the End of Time'
+- 'Star Ocean III: Till the End of Time'
+https://www.youtube.com/watch?v=Fa9-pSBa9Cg:
+- Gran Turismo 5
+- Gran Turismo V
+https://www.youtube.com/watch?v=Ff_r_6N8PII:
+- 'The Legend of Zelda: Twilight Princess'
+- twilight princess
+https://www.youtube.com/watch?v=FgQaK7TGjX4:
+- 'The Legend of Zelda: A Link to the Past'
+- a link to the past
+https://www.youtube.com/watch?v=FjFx5oO-riE:
+- 'Castlevania: Order of Ecclesia'
+https://www.youtube.com/watch?v=FkMm63VAHps:
+- Gunlord
+https://www.youtube.com/watch?v=FnogL42dEL4:
+- 'Command & Conquer: Red Alert'
+- 'command and conquer: red alert'
+https://www.youtube.com/watch?v=FqrNEjtl2FI:
+- Twinsen's Odyssey
+https://www.youtube.com/watch?v=FrhLXDBP-2Q:
+- DuckTales
+https://www.youtube.com/watch?v=Fs9FhHHQKwE:
+- Chrono Cross
+https://www.youtube.com/watch?v=Ft-qnOD77V4:
+- Doom
+https://www.youtube.com/watch?v=G0YMlSu1DeA:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=G4g1SH2tEV0:
+- Aliens Incursion
+https://www.youtube.com/watch?v=GAVePrZeGTc:
+- SaGa Frontier
+https://www.youtube.com/watch?v=GBYsdw4Vwx8:
+- Silent Hill 2
+- Silent Hill II
+https://www.youtube.com/watch?v=GC99a8VRsIw:
+- Earthbound
+https://www.youtube.com/watch?v=GCiOjOMciOI:
+- LocoRoco
+https://www.youtube.com/watch?v=GEuRzRW86bI:
+- Musashi Samurai Legend
+https://www.youtube.com/watch?v=GIhf0yU94q4:
+- Mr. Nutz
+https://www.youtube.com/watch?v=GIuBC4GU6C8:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=GKFwm2NSJdc:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=GKKhBT0A1pE:
+- Wild Arms 3
+- Wild Arms III
+https://www.youtube.com/watch?v=GLPT6H4On4o:
+- Metroid Fusion
+https://www.youtube.com/watch?v=GM6lrZw9Fdg:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=GNqR9kGLAeA:
+- Croc 2
+- Croc II
+https://www.youtube.com/watch?v=GQND5Y7_pXc:
+- Sprint Vector
+https://www.youtube.com/watch?v=GRU4yR3FQww:
+- Final Fantasy XIII
+- Final Fantasy 13
+https://www.youtube.com/watch?v=GWaooxrlseo:
+- Mega Man X3
+https://www.youtube.com/watch?v=G_80PQ543rM:
+- DuckTales
+https://www.youtube.com/watch?v=GaOT77kUTD8:
+- Jack Bros.
+https://www.youtube.com/watch?v=GdOFuA2qpG4:
+- Mega Man Battle Network 3
+- Mega Man Battle Network III
+https://www.youtube.com/watch?v=GhFffRvPfig:
+- DuckTales
+https://www.youtube.com/watch?v=Gibt8OLA__M:
+- Pilotwings 64
+https://www.youtube.com/watch?v=GnwlAOp6tfI:
+- L.A. Noire
+- 50.A. Noire
+https://www.youtube.com/watch?v=Gt-QIiYkAOU:
+- Galactic Pinball
+https://www.youtube.com/watch?v=GtZNgj5OUCM:
+- 'Pokemon Mystery Dungeon: Explorers of Sky'
+https://www.youtube.com/watch?v=GyiSanVotOM:
+- Dark Souls II
+- Dark Souls 2
+https://www.youtube.com/watch?v=GyuReqv2Rnc:
+- ActRaiser
+https://www.youtube.com/watch?v=GzBsFGh6zoc:
+- Lufia
+https://www.youtube.com/watch?v=Gza34GxrZlk:
+- Secret of the Stars
+https://www.youtube.com/watch?v=H-CwNdgHcDw:
+- Lagoon
+https://www.youtube.com/watch?v=H1B52TSCl_A:
+- Super Mario 64
+https://www.youtube.com/watch?v=H2-rCJmEDIQ:
+- Fez
+https://www.youtube.com/watch?v=H3fQtYVwpx4:
+- Croc 2
+- Croc II
+https://www.youtube.com/watch?v=H4hryHF3kTw:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=HCHsMo4BOJ0:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=HCi2-HtFh78:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=HFKtYCcMWT4:
+- Mega Man 2
+- Mega Man II
+https://www.youtube.com/watch?v=HGMjMDE-gAY:
+- Terranigma
+https://www.youtube.com/watch?v=HHun7iYtbFU:
+- Mega Man 6
+- Mega Man VI
+https://www.youtube.com/watch?v=HIKOSJh9_5k:
+- Treasure Hunter G
+https://www.youtube.com/watch?v=HImC0q17Pk0:
+- 'FTL: Faster Than Light'
+https://www.youtube.com/watch?v=HLl-oMBhiPc:
+- 'Sailor Moon RPG: Another Story'
+https://www.youtube.com/watch?v=HNPqugUrdEM:
+- 'Castlevania: Lament of Innocence'
+https://www.youtube.com/watch?v=HO0rvkOPQww:
+- Guardian's Crusade
+https://www.youtube.com/watch?v=HRAnMmwuLx4:
+- Radiant Historia
+https://www.youtube.com/watch?v=HSWAPWjg5AM:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=HTo_H7376Rs:
+- Dark Souls II
+- Dark Souls 2
+https://www.youtube.com/watch?v=HUpDqe4s4do:
+- 'Lunar: The Silver Star'
+https://www.youtube.com/watch?v=HUzLO2GpPv4:
+- Castlevania 64
+https://www.youtube.com/watch?v=HW5WcFpYDc4:
+- 'Mega Man Battle Network 5: Double Team'
+- 'Mega Man Battle Network V: Double Team'
+https://www.youtube.com/watch?v=HXxA7QJTycA:
+- Mario Kart Wii
+https://www.youtube.com/watch?v=HZ9O1Gh58vI:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=H_rMLATTMws:
+- Illusion of Gaia
+https://www.youtube.com/watch?v=HaRmFcPG7o8:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=Hbw3ZVY7qGc:
+- Scott Pilgrim vs the World
+https://www.youtube.com/watch?v=He7jFaamHHk:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=HeirTA9Y9i0:
+- Maken X
+https://www.youtube.com/watch?v=HijQBbE0WiQ:
+- Heroes of Might and Magic III
+- Heroes of Might and Magic 3
+https://www.youtube.com/watch?v=HmOUI30QqiE:
+- Streets of Rage 2
+- Streets of Rage II
+https://www.youtube.com/watch?v=HneWfB9jsHk:
+- Suikoden III
+- Suikoden 3
+https://www.youtube.com/watch?v=Ho2TE9ZfkgI:
+- Battletoads (Arcade)
+- battletoads
+https://www.youtube.com/watch?v=HpecW3jSJvQ:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=Hro03nOyUuI:
+- Ecco the Dolphin (Sega CD)
+- Ecco the Dolphin
+https://www.youtube.com/watch?v=HtJPpVCuYGQ:
+- 'Chuck Rock II: Son of Chuck'
+- 'Chuck Rock 2: Son of Chuck'
+https://www.youtube.com/watch?v=HvnAkAQK82E:
+- Tales of Symphonia
+https://www.youtube.com/watch?v=HvyPtN7_jNk:
+- F-Zero GX
+https://www.youtube.com/watch?v=HwBOvdH38l0:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=HyWy1vzcqGE:
+- Boom Blox
+https://www.youtube.com/watch?v=I-_yzFMnclM:
+- 'Lufia: The Legend Returns'
+https://www.youtube.com/watch?v=I0FNN-t4pRU:
+- Earthbound
+https://www.youtube.com/watch?v=I0rfWwuyHFg:
+- Time Trax
+https://www.youtube.com/watch?v=I1USJ16xqw4:
+- Disney's Aladdin
+https://www.youtube.com/watch?v=I2LzT-9KtNA:
+- The Oregon Trail
+https://www.youtube.com/watch?v=I4b8wCqmQfE:
+- Phantasy Star Online Episode III
+- Phantasy Star Online Episode 3
+https://www.youtube.com/watch?v=I4gMnPkOQe8:
+- Earthbound
+https://www.youtube.com/watch?v=I5GznpBjHE4:
+- Soul Blazer
+https://www.youtube.com/watch?v=I8ij2RGGBtc:
+- Mario Sports Mix
+https://www.youtube.com/watch?v=I8zZaUvkIFY:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=ICOotKB_MUc:
+- Top Gear
+https://www.youtube.com/watch?v=ICdhgaXXor4:
+- Mass Effect 3
+- Mass Effect III
+https://www.youtube.com/watch?v=IDUGJ22OWLE:
+- Front Mission 1st
+https://www.youtube.com/watch?v=IE3FTu_ppko:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=IEMaS33Wcd8:
+- Shovel Knight
+https://www.youtube.com/watch?v=IEf1ALD_TCA:
+- Bully
+https://www.youtube.com/watch?v=IQDiMzoTMH4:
+- 'World of Warcraft: Wrath of the Lich King'
+https://www.youtube.com/watch?v=IS-bZp4WW4w:
+- Crusader of Centy
+https://www.youtube.com/watch?v=ITQVlvGsSDA:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=ITijTBoqJpc:
+- 'Final Fantasy Fables: Chocobo''s Dungeon'
+https://www.youtube.com/watch?v=IUAZtA-hHsw:
+- SaGa Frontier II
+- SaGa Frontier 2
+https://www.youtube.com/watch?v=IVCQ-kau7gs:
+- Shatterhand
+https://www.youtube.com/watch?v=IWoCTYqBOIE:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=IXU7Jhi6jZ8:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=IY7hfsfPh84:
+- Radiata Stories
+https://www.youtube.com/watch?v=IYGLnkSrA50:
+- Super Paper Mario
+https://www.youtube.com/watch?v=Ia1BEcALLX0:
+- Radiata Stories
+https://www.youtube.com/watch?v=IbBmFShDBzs:
+- Secret of Mana
+https://www.youtube.com/watch?v=Ie1zB5PHwEw:
+- Contra
+https://www.youtube.com/watch?v=IgPtGSdLliQ:
+- Chrono Trigger
+https://www.youtube.com/watch?v=IjZaRBbmVUc:
+- Bastion
+https://www.youtube.com/watch?v=ImdjNeH310w:
+- Ragnarok Online II
+- Ragnarok Online 2
+https://www.youtube.com/watch?v=IrLs8cW3sIc:
+- 'The Legend of Zelda: Wind Waker'
+- wind waker
+https://www.youtube.com/watch?v=Ir_3DdPZeSg:
+- 'Far Cry 3: Blood Dragon'
+- 'Far Cry III: Blood Dragon'
+https://www.youtube.com/watch?v=Is_yOYLMlHY:
+- "We \xE2\u2122\xA5 Katamari"
+https://www.youtube.com/watch?v=Iss6CCi3zNk:
+- Earthbound
+https://www.youtube.com/watch?v=Iu4MCoIyV94:
+- Motorhead
+https://www.youtube.com/watch?v=IwIt4zlHSWM:
+- Donkey Kong Country 3
+- Donkey Kong Country III
+https://www.youtube.com/watch?v=IyAs15CjGXU:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=J-zD9QjtRNo:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=J1x1Ao6CxyA:
+- Double Dragon II
+- Double Dragon 2
+https://www.youtube.com/watch?v=J323aFuwx64:
+- Super Mario Bros 3
+- Super Mario Bros III
+https://www.youtube.com/watch?v=J4EE4hRA9eU:
+- Lost Odyssey
+https://www.youtube.com/watch?v=J67nkzoJ_2M:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=J6Beh5YUWdI:
+- Wario World
+https://www.youtube.com/watch?v=JA_VeKxyfiU:
+- Golden Sun
+https://www.youtube.com/watch?v=JCqSHvyY_2I:
+- Secret of Evermore
+https://www.youtube.com/watch?v=JE1hhd0E-_I:
+- Lufia II
+- Lufia 2
+https://www.youtube.com/watch?v=JF7ucc5IH_Y:
+- Mass Effect
+https://www.youtube.com/watch?v=JFadABMZnYM:
+- Ragnarok Online
+https://www.youtube.com/watch?v=JGQ_Z0W43D4:
+- Secret of Evermore
+https://www.youtube.com/watch?v=JHrGsxoZumY:
+- Soukaigi
+https://www.youtube.com/watch?v=JKVUavdztAg:
+- Shovel Knight
+https://www.youtube.com/watch?v=JOFsATsPiH0:
+- Deus Ex
+https://www.youtube.com/watch?v=JRKOBmaENoE:
+- Spanky's Quest
+https://www.youtube.com/watch?v=JS8vCLXX5Pg:
+- Panzer Dragoon Saga
+https://www.youtube.com/watch?v=JV8qMsWKTvk:
+- Legaia 2
+- Legaia II
+https://www.youtube.com/watch?v=JWMtsksJtYw:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=JXtWCWeM6uI:
+- Animal Crossing
+https://www.youtube.com/watch?v=J_cTMwAZil0:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=Jb83GX7k_08:
+- Shadow of the Colossus
+https://www.youtube.com/watch?v=Je3YoGKPC_o:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=Jg5M1meS6wI:
+- Metal Gear Solid
+https://www.youtube.com/watch?v=JgGPRcUgGNk:
+- Ace Combat 6
+- Ace Combat VI
+https://www.youtube.com/watch?v=Jgm24MNQigg:
+- Parasite Eve II
+- Parasite Eve 2
+https://www.youtube.com/watch?v=JhDblgtqx5o:
+- Arumana no Kiseki
+https://www.youtube.com/watch?v=Jig-PUAk-l0:
+- Final Fantasy Adventure
+https://www.youtube.com/watch?v=JkEt-a3ro1U:
+- Final Fantasy Adventure
+https://www.youtube.com/watch?v=JlGnZvt5OBE:
+- Ittle Dew
+https://www.youtube.com/watch?v=Jokz0rBmE7M:
+- ZombiU
+https://www.youtube.com/watch?v=Jq949CcPxnM:
+- Super Valis IV
+- Super Valis 4
+https://www.youtube.com/watch?v=JrlGy3ozlDA:
+- Deep Fear
+https://www.youtube.com/watch?v=JsjTpjxb9XU:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=JvGhaOX-aOo:
+- Mega Man & Bass
+- mega man and bass
+https://www.youtube.com/watch?v=JvMsfqT9KB8:
+- Hotline Miami
+https://www.youtube.com/watch?v=JwSV7nP5wcs:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=JxhYFSN_xQY:
+- Musashi Samurai Legend
+https://www.youtube.com/watch?v=Jz2sxbuN3gM:
+- 'Moon: Remix RPG Adventure'
+https://www.youtube.com/watch?v=JzPKClyQ1rQ:
+- Persona 3 Portable
+- Persona III Portable
+https://www.youtube.com/watch?v=K2fx7bngK80:
+- Thumper
+https://www.youtube.com/watch?v=K5AOu1d79WA:
+- 'Ecco the Dolphin: Defender of the Future'
+https://www.youtube.com/watch?v=KAHuWEfue8U:
+- Wild Arms 3
+- Wild Arms III
+https://www.youtube.com/watch?v=KB0Yxdtig90:
+- 'Hitman 2: Silent Assassin'
+- 'Hitman II: Silent Assassin'
+https://www.youtube.com/watch?v=KDVUlqp8aFM:
+- Earthworm Jim
+https://www.youtube.com/watch?v=KI6ZwWinXNM:
+- 'Digimon Story: Cyber Sleuth'
+https://www.youtube.com/watch?v=KTHBvQKkibA:
+- The 7th Saga
+https://www.youtube.com/watch?v=KULCV3TgyQk:
+- Breath of Fire
+https://www.youtube.com/watch?v=KWH19AGkFT0:
+- Dark Cloud
+https://www.youtube.com/watch?v=KWIbZ_4k3lE:
+- Final Fantasy III
+- Final Fantasy 3
+https://www.youtube.com/watch?v=KWRoyFQ1Sj4:
+- Persona 5
+- Persona V
+https://www.youtube.com/watch?v=KX57tzysYQc:
+- Metal Gear 2
+- Metal Gear II
+https://www.youtube.com/watch?v=KYfK61zxads:
+- Angry Video Game Nerd Adventures
+https://www.youtube.com/watch?v=KZA1PegcwIg:
+- Goldeneye
+https://www.youtube.com/watch?v=KZyPC4VPWCY:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=K_jigRSuN-g:
+- MediEvil
+https://www.youtube.com/watch?v=Kc7UynA9WVk:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=KgCLAXQow3w:
+- Ghouls 'n' Ghosts
+https://www.youtube.com/watch?v=KiGfjOLF_j0:
+- Earthbound
+https://www.youtube.com/watch?v=Kk0QDbYvtAQ:
+- Arcana
+https://www.youtube.com/watch?v=Km-cCxquP-s:
+- Yoshi's Island
+https://www.youtube.com/watch?v=KnTyM5OmRAM:
+- 'Mario & Luigi: Partners in Time'
+- 'mario and luigi: partners in time'
+https://www.youtube.com/watch?v=KnoUxId8yUQ:
+- Jak & Daxter
+- jak and daxter
+https://www.youtube.com/watch?v=KoPF_wOrQ6A:
+- 'Tales from Space: Mutant Blobs Attack'
+https://www.youtube.com/watch?v=Kr5mloai1B0:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=KrvdivSD98k:
+- Sonic the Hedgehog 3
+- Sonic the Hedgehog III
+https://www.youtube.com/watch?v=Ks9ZCaUNKx4:
+- Quake II
+- Quake 2
+https://www.youtube.com/watch?v=L5t48bbzRDs:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=L8TEsGhBOfI:
+- The Binding of Isaac
+https://www.youtube.com/watch?v=L9Uft-9CV4g:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=L9e6Pye7p5A:
+- Mystical Ninja Starring Goemon
+https://www.youtube.com/watch?v=LAHKscXvt3Q:
+- Space Harrier
+https://www.youtube.com/watch?v=LD4OAYQx1-I:
+- Metal Gear Solid 4
+- Metal Gear Solid IV
+https://www.youtube.com/watch?v=LDvKwSVuUGA:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=LGLW3qgiiB8:
+- "We \xE2\u2122\xA5 Katamari"
+https://www.youtube.com/watch?v=LPO5yrMSMEw:
+- Ridge Racers
+https://www.youtube.com/watch?v=LQ0uDk5i_s0:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=LQxlUTTrKWE:
+- 'Assassin''s Creed III: The Tyranny of King Washington'
+- 'Assassin''s Creed 3: The Tyranny of King Washington'
+https://www.youtube.com/watch?v=LSFho-sCOp0:
+- Grandia
+https://www.youtube.com/watch?v=LScvuN6-ZWo:
+- Battletoads & Double Dragon
+- battletoads
+- double dragon
+https://www.youtube.com/watch?v=LSfbb3WHClE:
+- No More Heroes
+https://www.youtube.com/watch?v=LTWbJDROe7A:
+- Xenoblade Chronicles X
+- Xenoblade Chronicles 10
+https://www.youtube.com/watch?v=LUjxPj3al5U:
+- Blue Dragon
+https://www.youtube.com/watch?v=LXuXWMV2hW4:
+- Metroid AM2R
+https://www.youtube.com/watch?v=LYiwMd5y78E:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=LcFX7lFjfqA:
+- Shin Megami Tensei IV
+- Shin Megami Tensei 4
+https://www.youtube.com/watch?v=LdIlCX2iOa0:
+- Antichamber
+https://www.youtube.com/watch?v=Lj8ouSLvqzU:
+- Megalomachia 2
+- Megalomachia II
+https://www.youtube.com/watch?v=LkRfePyfoj4:
+- Senko no Ronde (Wartech)
+- wartech
+- senko no ronde
+https://www.youtube.com/watch?v=LlokRNcURKM:
+- The Smurfs' Nightmare
+https://www.youtube.com/watch?v=LpO2ar64UGE:
+- Mega Man 9
+- Mega Man IX
+https://www.youtube.com/watch?v=LpxUHj-a_UI:
+- Michael Jackson's Moonwalker
+https://www.youtube.com/watch?v=Luko2A5gNpk:
+- Metroid Prime
+https://www.youtube.com/watch?v=Lx906iVIZSE:
+- 'Diablo III: Reaper of Souls'
+- 'Diablo 3: Reaper of Souls'
+https://www.youtube.com/watch?v=M16kCIMiNyc:
+- 'Lisa: The Painful RPG'
+https://www.youtube.com/watch?v=M3FytW43iOI:
+- Fez
+https://www.youtube.com/watch?v=M3Wux3163kI:
+- 'Castlevania: Dracula X'
+https://www.youtube.com/watch?v=M4FN0sBxFoE:
+- Arc the Lad
+https://www.youtube.com/watch?v=M4XYQ8YfVdo:
+- Rollercoaster Tycoon 3
+- Rollercoaster Tycoon III
+https://www.youtube.com/watch?v=MCITsL-vfW8:
+- Miitopia
+https://www.youtube.com/watch?v=MH00uDOwD28:
+- Portal 2
+- Portal II
+https://www.youtube.com/watch?v=MK41-UzpQLk:
+- Star Fox 64
+https://www.youtube.com/watch?v=ML-kTPHnwKI:
+- Metroid
+https://www.youtube.com/watch?v=MLFX_CIsvS8:
+- Final Fantasy Adventure
+https://www.youtube.com/watch?v=MPvQoxXUQok:
+- Final Fantasy Tactics
+https://www.youtube.com/watch?v=MTThoxoAVoI:
+- NieR
+https://www.youtube.com/watch?v=MYNeu0cZ3NE:
+- Silent Hill
+https://www.youtube.com/watch?v=M_B1DJu8FlQ:
+- Super Mario Odyssey
+https://www.youtube.com/watch?v=MaiHaXRYtNQ:
+- Tales of Vesperia
+https://www.youtube.com/watch?v=MbkMki62A4o:
+- Gran Turismo 5
+- Gran Turismo V
+https://www.youtube.com/watch?v=Mea-D-VFzck:
+- 'Castlevania: Dawn of Sorrow'
+https://www.youtube.com/watch?v=MffE4TpsHto:
+- Assassin's Creed II
+- Assassin's Creed 2
+https://www.youtube.com/watch?v=MfsFZsPiw3M:
+- Grandia
+https://www.youtube.com/watch?v=Mg236zrHA40:
+- Dragon Quest VIII
+- Dragon Quest 8
+https://www.youtube.com/watch?v=MhjEEbyuJME:
+- Xenogears
+https://www.youtube.com/watch?v=MjeghICMVDY:
+- Ape Escape 3
+- Ape Escape III
+https://www.youtube.com/watch?v=MkKh-oP7DBQ:
+- Waterworld
+https://www.youtube.com/watch?v=MlRGotA3Yzs:
+- Mega Man 4
+- Mega Man IV
+https://www.youtube.com/watch?v=MlhPnFjCfwc:
+- Blue Stinger
+https://www.youtube.com/watch?v=Mn3HPClpZ6Q:
+- 'Vampire The Masquerade: Bloodlines'
+https://www.youtube.com/watch?v=Mobwl45u2J8:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=MowlJduEbgY:
+- Ori and the Blind Forest
+https://www.youtube.com/watch?v=MqK5MvPwPsE:
+- Hotline Miami
+https://www.youtube.com/watch?v=MthR2dXqWHI:
+- Super Mario RPG
+https://www.youtube.com/watch?v=MvJUxw8rbPA:
+- 'The Elder Scrolls III: Morrowind'
+- 'The Elder Scrolls 3: Morrowind'
+- Morrowind
+https://www.youtube.com/watch?v=MxShFnOgCnk:
+- Soma Bringer
+https://www.youtube.com/watch?v=MxyCk1mToY4:
+- Koudelka
+https://www.youtube.com/watch?v=N-BiX7QXE8k:
+- Shin Megami Tensei Nocturne
+https://www.youtube.com/watch?v=N-T8KwCCNh8:
+- Advance Wars DS
+https://www.youtube.com/watch?v=N1EyCv65yOI:
+- 'Qbeh-1: The Atlas Cube'
+- 'Qbeh-I: The Atlas Cube'
+https://www.youtube.com/watch?v=N1lp6YLpT_o:
+- Tribes 2
+- Tribes II
+https://www.youtube.com/watch?v=N2_yNExicyI:
+- 'Castlevania: Curse of Darkness'
+https://www.youtube.com/watch?v=N46rEikk4bw:
+- Ecco the Dolphin (Sega CD)
+- Ecco the Dolphin
+https://www.youtube.com/watch?v=N4V4OxH1d9Q:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=N6hzEQyU6QM:
+- Grounseed
+https://www.youtube.com/watch?v=N74vegXRMNk:
+- Bayonetta
+https://www.youtube.com/watch?v=NAyXKITCss8:
+- Forza Motorsport 4
+- Forza Motorsport IV
+https://www.youtube.com/watch?v=NFsvEFkZHoE:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=NGJp1-tPT54:
+- OutRun2
+https://www.youtube.com/watch?v=NL0AZ-oAraw:
+- Terranigma
+https://www.youtube.com/watch?v=NOomtJrX_MA:
+- Western Lords
+https://www.youtube.com/watch?v=NP3EK1Kr1sQ:
+- Dr. Mario
+https://www.youtube.com/watch?v=NRNHbaF_bvY:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=NT-c2ZeOpsg:
+- Sonic Unleashed
+https://www.youtube.com/watch?v=NTfVsOnX0lY:
+- Rayman
+https://www.youtube.com/watch?v=NUloEiKpAZU:
+- Goldeneye
+https://www.youtube.com/watch?v=NVRgpAmkmPg:
+- Resident Evil 4
+- Resident Evil IV
+https://www.youtube.com/watch?v=NXr5V6umW6U:
+- Opoona
+https://www.youtube.com/watch?v=NZVZC4x9AzA:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=NcjcKZnJqAA:
+- 'The Legend of Zelda: Minish Cap'
+- minish cap
+https://www.youtube.com/watch?v=Nd2O6mbhCLU:
+- Mega Man 5
+- Mega Man V
+https://www.youtube.com/watch?v=NgKT8GTKhYU:
+- 'Final Fantasy XI: Wings of the Goddess'
+- 'Final Fantasy 11: Wings of the Goddess'
+https://www.youtube.com/watch?v=Nhj0Rct8v48:
+- The Wonderful 101
+- The Wonderful CI
+https://www.youtube.com/watch?v=NjG2ZjPqzzE:
+- Journey to Silius
+https://www.youtube.com/watch?v=NjmUCbNk65o:
+- Donkey Kong Country 3
+- Donkey Kong Country III
+https://www.youtube.com/watch?v=NkonFpRLGTU:
+- Transistor
+https://www.youtube.com/watch?v=NlsRts7Sims:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=Nn5K-NNmgTM:
+- Chrono Trigger
+https://www.youtube.com/watch?v=NnZlRu28fcU:
+- Etrian Odyssey
+https://www.youtube.com/watch?v=NnvD6RDF-SI:
+- Chibi-Robo
+https://www.youtube.com/watch?v=NnvZ6Dqv7Ws:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=Nr2McZBfSmc:
+- Uninvited
+https://www.youtube.com/watch?v=NtRlpjt5ItA:
+- Lone Survivor
+https://www.youtube.com/watch?v=NtXv9yFZI_Y:
+- Earthbound
+https://www.youtube.com/watch?v=Nu91ToSI4MU:
+- Breath of Death VII
+- Breath of Death 7
+https://www.youtube.com/watch?v=NuSPcCqiCZo:
+- Pilotwings 64
+https://www.youtube.com/watch?v=Nw5cfSRvFSA:
+- Super Meat Boy
+https://www.youtube.com/watch?v=Nw7bbb1mNHo:
+- NeoTokyo
+https://www.youtube.com/watch?v=NxWGa33zC8w:
+- Alien Breed
+https://www.youtube.com/watch?v=O-v3Df_q5QQ:
+- Star Fox Adventures
+https://www.youtube.com/watch?v=O0fQlDmSSvY:
+- Opoona
+https://www.youtube.com/watch?v=O0kjybFXyxM:
+- Final Fantasy X-2
+- Final Fantasy 10-2
+- Final Fantasy X-II
+https://www.youtube.com/watch?v=O0rVK4H0-Eo:
+- Legaia 2
+- Legaia II
+https://www.youtube.com/watch?v=O1Ysg-0v7aQ:
+- Tekken 2
+- Tekken II
+https://www.youtube.com/watch?v=O5a4jwv-jPo:
+- Teenage Mutant Ninja Turtles
+https://www.youtube.com/watch?v=O8jJJXgNLo4:
+- Diablo I & II
+- Diablo 1 & 2
+- diablo 1
+- diablo 2
+- diablo I
+- diablo II
+https://www.youtube.com/watch?v=OB9t0q4kkEE:
+- Katamari Damacy
+https://www.youtube.com/watch?v=OCFWEWW9tAo:
+- SimCity
+https://www.youtube.com/watch?v=OD49e9J3qbw:
+- 'Resident Evil: Revelations'
+https://www.youtube.com/watch?v=ODjYdlmwf1E:
+- 'The Binding of Isaac: Rebirth'
+https://www.youtube.com/watch?v=OIsI5kUyLcI:
+- Super Mario Maker
+https://www.youtube.com/watch?v=OJjsUitjhmU:
+- 'Chuck Rock II: Son of Chuck'
+- 'Chuck Rock 2: Son of Chuck'
+https://www.youtube.com/watch?v=OMsJdryIOQU:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=OUmeK282f-E:
+- 'Silent Hill 4: The Room'
+- 'Silent Hill IV: The Room'
+https://www.youtube.com/watch?v=OViAthHme2o:
+- The Binding of Isaac
+https://www.youtube.com/watch?v=OWQ4bzYMbNQ:
+- Devil May Cry 3
+- Devil May Cry III
+https://www.youtube.com/watch?v=OXHIuTm-w2o:
+- Super Mario RPG
+https://www.youtube.com/watch?v=OXWqqshHuYs:
+- La-Mulana
+https://www.youtube.com/watch?v=OXqxg3FpuDA:
+- Ollie King
+https://www.youtube.com/watch?v=OYr-B_KWM50:
+- Mega Man 3
+- Mega Man III
+https://www.youtube.com/watch?v=OZLXcCe7GgA:
+- Ninja Gaiden
+https://www.youtube.com/watch?v=Oam7ttk5RzA:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=OegoI_0vduQ:
+- Mystical Ninja Starring Goemon
+https://www.youtube.com/watch?v=OjRNSYsddz0:
+- Yo-Kai Watch
+https://www.youtube.com/watch?v=Ol9Ine1TkEk:
+- Dark Souls
+https://www.youtube.com/watch?v=OmMWlRu6pbM:
+- 'Call of Duty: Black Ops II'
+- 'Call of Duty: Black Ops 2'
+- black ops 2
+https://www.youtube.com/watch?v=On1N8hL95Xw:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=Op2h7kmJw10:
+- 'Castlevania: Dracula X'
+https://www.youtube.com/watch?v=OpvLr9vyOhQ:
+- Undertale
+https://www.youtube.com/watch?v=OqXhJ_eZaPI:
+- Breath of Fire III
+- Breath of Fire 3
+https://www.youtube.com/watch?v=Os2q1_PkgzY:
+- Super Adventure Island
+https://www.youtube.com/watch?v=Ou0WU5p5XkI:
+- 'Atelier Iris 3: Grand Phantasm'
+- 'Atelier Iris III: Grand Phantasm'
+https://www.youtube.com/watch?v=Ovn18xiJIKY:
+- Dragon Quest VI
+- Dragon Quest 6
+https://www.youtube.com/watch?v=OzbSP7ycMPM:
+- Yoshi's Island
+https://www.youtube.com/watch?v=P01-ckCZtCo:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=P1IBUrLte2w:
+- Earthbound 64
+https://www.youtube.com/watch?v=P2smOsHacjk:
+- The Magical Land of Wozz
+https://www.youtube.com/watch?v=P3FU2NOzUEU:
+- Paladin's Quest
+https://www.youtube.com/watch?v=P3oYGDwIKbc:
+- The Binding of Isaac
+https://www.youtube.com/watch?v=P3vzN5sizXk:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=P7K7jzxf6iw:
+- Legend of Legaia
+https://www.youtube.com/watch?v=P8oefrmJrWk:
+- Metroid Prime
+https://www.youtube.com/watch?v=P9OdKnQQchQ:
+- 'The Elder Scrolls IV: Oblivion'
+- 'The Elder Scrolls 4: Oblivion'
+- Oblivion
+https://www.youtube.com/watch?v=PAU7aZ_Pz4c:
+- Heimdall 2
+- Heimdall II
+https://www.youtube.com/watch?v=PAuFr7PZtGg:
+- Tales of Legendia
+https://www.youtube.com/watch?v=PFQCO_q6kW8:
+- Donkey Kong 64
+https://www.youtube.com/watch?v=PGowEQXyi3Y:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=PKno6qPQEJg:
+- Blaster Master
+https://www.youtube.com/watch?v=PMKc5Ffynzw:
+- 'Kid Icarus: Uprising'
+https://www.youtube.com/watch?v=POAGsegLMnA:
+- Super Mario Land
+https://www.youtube.com/watch?v=PQjOIZTv8I4:
+- Super Street Fighter II Turbo
+- Super Street Fighter 2 Turbo
+https://www.youtube.com/watch?v=PRCBxcvNApQ:
+- Glover
+https://www.youtube.com/watch?v=PRLWoJBwJFY:
+- World of Warcraft
+https://www.youtube.com/watch?v=PUZ8r9MJczQ:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=PXqJEm-vm-w:
+- Tales of Vesperia
+https://www.youtube.com/watch?v=PZBltehhkog:
+- 'MediEvil: Resurrection'
+https://www.youtube.com/watch?v=PZnF6rVTgQE:
+- Dragon Quest VII
+- Dragon Quest 7
+https://www.youtube.com/watch?v=PZwTdBbDmB8:
+- Mother
+https://www.youtube.com/watch?v=Pc3GfVHluvE:
+- Baten Kaitos
+https://www.youtube.com/watch?v=PfY_O8NPhkg:
+- 'Bravely Default: Flying Fairy'
+https://www.youtube.com/watch?v=PhW7tlUisEU:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=Pjdvqy1UGlI:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=PjlkqeMdZzU:
+- Paladin's Quest II
+- Paladin's Quest 2
+https://www.youtube.com/watch?v=PkKXW2-3wvg:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=Pmn-r3zx-E0:
+- Katamari Damacy
+https://www.youtube.com/watch?v=Poh9VDGhLNA:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=PqvQvt3ePyg:
+- Live a Live
+https://www.youtube.com/watch?v=PwEzeoxbHMQ:
+- Donkey Kong Country 3
+- Donkey Kong Country III
+https://www.youtube.com/watch?v=PwlvY_SeUQU:
+- Starcraft
+https://www.youtube.com/watch?v=PyubBPi9Oi0:
+- Pokemon
+https://www.youtube.com/watch?v=PzkbuitZEjE:
+- Ragnarok Online
+https://www.youtube.com/watch?v=Q1TnrjE909c:
+- 'Lunar: Dragon Song'
+https://www.youtube.com/watch?v=Q27un903ps0:
+- F-Zero GX
+https://www.youtube.com/watch?v=Q3Vci9ri4yM:
+- Cyborg 009
+https://www.youtube.com/watch?v=Q7a0piUG3jM:
+- Dragon Ball Z Butouden 2
+- Dragon Ball Z Butouden II
+https://www.youtube.com/watch?v=QD30b0MwpQw:
+- Shatter
+https://www.youtube.com/watch?v=QGgK5kQkLUE:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=QHStTXLP7II:
+- Breath of Fire IV
+- Breath of Fire 4
+https://www.youtube.com/watch?v=QLsVsiWgTMo:
+- 'Mario Kart: Double Dash!!'
+https://www.youtube.com/watch?v=QN1wbetaaTk:
+- Kirby & The Rainbow Curse
+- kirby and the rainbow curse
+https://www.youtube.com/watch?v=QNd4WYmj9WI:
+- 'World of Warcraft: Wrath of the Lich King'
+https://www.youtube.com/watch?v=QOKl7-it2HY:
+- Silent Hill
+https://www.youtube.com/watch?v=QR5xn8fA76Y:
+- Terranigma
+https://www.youtube.com/watch?v=QTwpZhWtQus:
+- 'The Elder Scrolls IV: Oblivion'
+- 'The Elder Scrolls 4: Oblivion'
+- Oblivion
+https://www.youtube.com/watch?v=QXd1P54rIzQ:
+- Secret of Evermore
+https://www.youtube.com/watch?v=QYnrEDKTB-k:
+- The Guardian Legend
+https://www.youtube.com/watch?v=QZiTBVot5xE:
+- Legaia 2
+- Legaia II
+https://www.youtube.com/watch?v=QaE0HHN4c30:
+- Halo
+https://www.youtube.com/watch?v=QdLD2Wop_3k:
+- Super Paper Mario
+https://www.youtube.com/watch?v=QiX-xWrkNZs:
+- Machinarium
+https://www.youtube.com/watch?v=QkgA1qgTsdQ:
+- Contact
+https://www.youtube.com/watch?v=Ql-Ud6w54wc:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=Qnz_S0QgaLA:
+- Deus Ex
+https://www.youtube.com/watch?v=QqN7bKgYWI0:
+- Suikoden
+https://www.youtube.com/watch?v=QtW1BBAufvM:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=QuSSx8dmAXQ:
+- Gran Turismo 4
+- Gran Turismo IV
+https://www.youtube.com/watch?v=Qv_8KiUsRTE:
+- Golden Sun
+https://www.youtube.com/watch?v=R1DRTdnR0qU:
+- Chrono Trigger
+https://www.youtube.com/watch?v=R2yEBE2ueuQ:
+- Breath of Fire
+https://www.youtube.com/watch?v=R3gmQcMK_zg:
+- Ragnarok Online
+https://www.youtube.com/watch?v=R5BZKMlqbPI:
+- Castlevania Curse of Darkness
+https://www.youtube.com/watch?v=R6BoWeWh2Fg:
+- 'Cladun: This is an RPG'
+https://www.youtube.com/watch?v=R6tANRv2YxA:
+- FantaVision (North America)
+- FantaVision
+https://www.youtube.com/watch?v=R6us0FiZoTU:
+- Final Fantasy Mystic Quest
+https://www.youtube.com/watch?v=R9rnsbf914c:
+- Lethal League
+https://www.youtube.com/watch?v=RAevlv9Y1ao:
+- Tales of Symphonia
+https://www.youtube.com/watch?v=RBKbYPqJNOw:
+- Wii Shop Channel
+https://www.youtube.com/watch?v=RBslMKpPu1M:
+- 'Donkey Kong Country: Tropical Freeze'
+https://www.youtube.com/watch?v=RBxWlVGd9zE:
+- Wild Arms 2
+- Wild Arms II
+https://www.youtube.com/watch?v=RHiQZ7tXSlw:
+- Radiant Silvergun
+https://www.youtube.com/watch?v=RKm11Z6Btbg:
+- Daytona USA
+https://www.youtube.com/watch?v=RNkUpb36KhQ:
+- Titan Souls
+https://www.youtube.com/watch?v=ROKcr2OTgws:
+- Chrono Cross
+https://www.youtube.com/watch?v=RO_FVqiEtDY:
+- Super Paper Mario
+https://www.youtube.com/watch?v=RP5DzEkA0l8:
+- Machinarium
+https://www.youtube.com/watch?v=RSlUnXWm9hM:
+- NeoTokyo
+https://www.youtube.com/watch?v=RSm22cu707w:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=RVBoUZgRG68:
+- Super Paper Mario
+https://www.youtube.com/watch?v=RXZ2gTXDwEc:
+- Donkey Kong Country 3
+- Donkey Kong Country III
+https://www.youtube.com/watch?v=Rj9bp-bp-TA:
+- Grow Home
+https://www.youtube.com/watch?v=RkDusZ10M4c:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=Roj5F9QZEpU:
+- 'Momodora: Reverie Under the Moonlight'
+https://www.youtube.com/watch?v=RpQlfTGfEsw:
+- Sonic CD
+- Sonic 400
+https://www.youtube.com/watch?v=RqqbUR7YxUw:
+- Pushmo
+https://www.youtube.com/watch?v=Rs2y4Nqku2o:
+- Chrono Cross
+https://www.youtube.com/watch?v=Ru7_ew8X6i4:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=Rv7-G28CPFI:
+- Castlevania Curse of Darkness
+https://www.youtube.com/watch?v=RxcQY1OUzzY:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=RyQAZcBim88:
+- Ape Escape 3
+- Ape Escape III
+https://www.youtube.com/watch?v=RypdLW4G1Ng:
+- Hot Rod
+https://www.youtube.com/watch?v=S-vNB8mR1B4:
+- Sword of Mana
+https://www.youtube.com/watch?v=S0TmwLeUuBw:
+- God Hand
+https://www.youtube.com/watch?v=S3k1zdbBhRQ:
+- Donkey Kong Land
+https://www.youtube.com/watch?v=S87W-Rnag1E:
+- 'The Legend of Zelda: Twilight Princess'
+- twilight princess
+https://www.youtube.com/watch?v=SA7NfjCWbZU:
+- Treasure Hunter G
+https://www.youtube.com/watch?v=SAWxsyvWcqs:
+- Super Mario Galaxy 2
+- Super Mario Galaxy II
+https://www.youtube.com/watch?v=SCdUSkq_imI:
+- Super Mario 64
+https://www.youtube.com/watch?v=SDUUpUB1eu8:
+- Super Princess Peach
+https://www.youtube.com/watch?v=SE4FuK4MHJs:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=SFCn8IpgiLY:
+- Solstice
+https://www.youtube.com/watch?v=SKtO8AZLp2I:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=SNYFdankntY:
+- Mario Kart 64
+https://www.youtube.com/watch?v=SOAsm2UqIIM:
+- Cuphead
+https://www.youtube.com/watch?v=SPBIT_SSzmI:
+- Mario Party
+https://www.youtube.com/watch?v=SXQsmY_Px8Q:
+- Guardian of Paradise
+https://www.youtube.com/watch?v=SYp2ic7v4FU:
+- Rise of the Triad (2013)
+- Rise of the Triad
+https://www.youtube.com/watch?v=SawlCRnYYC8:
+- Eek! The Cat
+https://www.youtube.com/watch?v=Se-9zpPonwM:
+- Shatter
+https://www.youtube.com/watch?v=SeYZ8Bjo7tw:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=Sht8cKbdf_g:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=Sime7JZrTl0:
+- Castlevania
+https://www.youtube.com/watch?v=SjEwSzmSNVo:
+- Shenmue II
+- Shenmue 2
+https://www.youtube.com/watch?v=Sp7xqhunH88:
+- Waterworld
+https://www.youtube.com/watch?v=SqWu2wRA-Rk:
+- Battle Squadron
+https://www.youtube.com/watch?v=SrINCHeDeGI:
+- Luigi's Mansion
+https://www.youtube.com/watch?v=SsFYXts6EeE:
+- Ar Tonelico
+https://www.youtube.com/watch?v=Su5Z-NHGXLc:
+- 'Lunar: Eternal Blue'
+https://www.youtube.com/watch?v=SuI_RSHfLIk:
+- Guardian's Crusade
+https://www.youtube.com/watch?v=SvCIrLZ8hkI:
+- The Walking Dead
+https://www.youtube.com/watch?v=Sw9BfnRv8Ik:
+- 'Dreamfall: The Longest Journey'
+https://www.youtube.com/watch?v=SwVfsXvFbno:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=SzksdwLmxmY:
+- Bomberman Quest
+https://www.youtube.com/watch?v=T18nAaO_rGs:
+- Dragon Quest Monsters
+https://www.youtube.com/watch?v=T1edn6OPNRo:
+- 'Ys: The Oath in Felghana'
+https://www.youtube.com/watch?v=T24J3gTL4vY:
+- Polymer
+https://www.youtube.com/watch?v=T586T6QFjqE:
+- 'Castlevania: Dawn of Sorrow'
+https://www.youtube.com/watch?v=T9kK9McaCoE:
+- Mushihimesama Futari
+https://www.youtube.com/watch?v=TBx-8jqiGfA:
+- Super Mario Bros
+https://www.youtube.com/watch?v=TEPaoDnS6AM:
+- Street Fighter III 3rd Strike
+- Street Fighter 3 3rd Strike
+https://www.youtube.com/watch?v=TFxtovEXNmc:
+- Bahamut Lagoon
+https://www.youtube.com/watch?v=TIzYqi_QFY8:
+- Legend of Dragoon
+https://www.youtube.com/watch?v=TJH9E2x87EY:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=TM3BIOvEJSw:
+- Eternal Sonata
+https://www.youtube.com/watch?v=TMhh7ApHESo:
+- Super Win the Game
+https://www.youtube.com/watch?v=TO1kcFmNtTc:
+- Lost Odyssey
+https://www.youtube.com/watch?v=TPW9GRiGTek:
+- Yoshi's Woolly World
+https://www.youtube.com/watch?v=TRdrbKasYz8:
+- Xenosaga
+https://www.youtube.com/watch?v=TS8q1pjWviA:
+- Star Fox
+https://www.youtube.com/watch?v=TSlDUPl7DoA:
+- 'Star Ocean 3: Till the End of Time'
+- 'Star Ocean III: Till the End of Time'
+https://www.youtube.com/watch?v=TTt_-gE9iPU:
+- Bravely Default
+https://www.youtube.com/watch?v=TUZU34Sss8o:
+- Pokemon
+https://www.youtube.com/watch?v=TVKAMsRwIUk:
+- Nintendo World Cup
+https://www.youtube.com/watch?v=TXEz-i-oORk:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=TXO9vzXnAeg:
+- Dragon Quest
+https://www.youtube.com/watch?v=TYjKjjgQPk8:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=T_HfcZMUR4k:
+- Doom
+https://www.youtube.com/watch?v=TaRAKfltBfo:
+- Etrian Odyssey IV
+- Etrian Odyssey 4
+https://www.youtube.com/watch?v=Tc6G2CdSVfs:
+- 'Hitman: Blood Money'
+https://www.youtube.com/watch?v=TcKSIuOSs4U:
+- The Legendary Starfy
+https://www.youtube.com/watch?v=TdiRoUoSM-E:
+- Soma Bringer
+https://www.youtube.com/watch?v=TdxJKAvFEIU:
+- Lost Odyssey
+https://www.youtube.com/watch?v=TidW2D0Mnpo:
+- Blast Corps
+https://www.youtube.com/watch?v=TioQJoZ8Cco:
+- Xenosaga III
+- Xenosaga 3
+https://www.youtube.com/watch?v=Tj04oRO-0Ws:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=TkEH0e07jg8:
+- Secret of Evermore
+https://www.youtube.com/watch?v=TlDaPnHXl_o:
+- 'The Seventh Seal: Dark Lord'
+https://www.youtube.com/watch?v=TlLIOD2rJMs:
+- Chrono Trigger
+https://www.youtube.com/watch?v=TmkijsJ8-Kg:
+- 'NieR: Automata'
+https://www.youtube.com/watch?v=Tms-MKKS714:
+- Dark Souls
+https://www.youtube.com/watch?v=Tq8TV1PqZvw:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=TrO0wRbqPUo:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=TssxHy4hbko:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=TtACPCoJ-4I:
+- Mega Man 9
+- Mega Man IX
+https://www.youtube.com/watch?v=Tug0cYK0XW0:
+- Shenmue
+https://www.youtube.com/watch?v=U2MqAWgqYJY:
+- Mystical Ninja Starring Goemon
+https://www.youtube.com/watch?v=U2XioVnZUlo:
+- Nintendo Land
+https://www.youtube.com/watch?v=U3FkappfRsQ:
+- Katamari Damacy
+https://www.youtube.com/watch?v=U4aEm6UufgY:
+- 'NyxQuest: Kindred Spirits'
+https://www.youtube.com/watch?v=U9z3oWS0Qo0:
+- 'Castlevania: Bloodlines'
+https://www.youtube.com/watch?v=UBCtM24yyYY:
+- Little Inferno
+https://www.youtube.com/watch?v=UC6_FirddSI:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=UHAEjRndMwg:
+- Machinarium
+https://www.youtube.com/watch?v=UOOmKmahDX4:
+- Super Mario Kart
+https://www.youtube.com/watch?v=UOdV3Ci46jY:
+- Run Saber
+https://www.youtube.com/watch?v=UPdZlmyedcI:
+- Castlevania 64
+https://www.youtube.com/watch?v=UQFiG9We23I:
+- Streets of Rage 2
+- Streets of Rage II
+https://www.youtube.com/watch?v=UWOTB6x_WAs:
+- Magical Tetris Challenge
+https://www.youtube.com/watch?v=UZ9Z0YwFkyk:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=U_Ox-uIbalo:
+- Bionic Commando
+https://www.youtube.com/watch?v=U_l3eYfpUQ0:
+- 'Resident Evil: Revelations'
+https://www.youtube.com/watch?v=Ubu3U44i5Ic:
+- Super Mario 64
+https://www.youtube.com/watch?v=UdKzw6lwSuw:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=UmTX3cPnxXg:
+- Super Mario 3D World
+https://www.youtube.com/watch?v=UmgTFGAPkXc:
+- Secret of Mana
+https://www.youtube.com/watch?v=Un0n0m8b3uE:
+- 'Ys: The Oath in Felghana'
+https://www.youtube.com/watch?v=UnyOHbOV-h0:
+- Dragon Ball Z Butouden 2
+- Dragon Ball Z Butouden II
+https://www.youtube.com/watch?v=UoBLfXPlyPA:
+- Shatter
+https://www.youtube.com/watch?v=UoDDUr6poOs:
+- World of Warcraft
+https://www.youtube.com/watch?v=UoEyt7S10Mo:
+- Legend of Dragoon
+https://www.youtube.com/watch?v=UqQQ8LlMd3s:
+- Final Fantasy
+https://www.youtube.com/watch?v=Urqrn3sZbHQ:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=Uu4KCLd5kdg:
+- 'Diablo III: Reaper of Souls'
+- 'Diablo 3: Reaper of Souls'
+https://www.youtube.com/watch?v=UxiG3triMd8:
+- Hearthstone
+https://www.youtube.com/watch?v=V07qVpXUhc0:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=V1YsfDO8lgY:
+- Pokemon
+https://www.youtube.com/watch?v=V2UKwNO9flk:
+- 'Fire Emblem: Radiant Dawn'
+https://www.youtube.com/watch?v=V2kV7vfl1SE:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=V39OyFbkevY:
+- Etrian Odyssey IV
+- Etrian Odyssey 4
+https://www.youtube.com/watch?v=V4JjpIUYPg4:
+- Street Fighter IV
+- Street Fighter 4
+https://www.youtube.com/watch?v=V4tmMcpWm_I:
+- Super Street Fighter IV
+- Super Street Fighter 4
+https://www.youtube.com/watch?v=VClUpC8BwJU:
+- 'Silent Hill: Downpour'
+https://www.youtube.com/watch?v=VHCxLYOFHqU:
+- Chrono Trigger
+https://www.youtube.com/watch?v=VMMxNt_-s8E:
+- River City Ransom
+https://www.youtube.com/watch?v=VMt6f3DvTaU:
+- Mighty Switch Force
+https://www.youtube.com/watch?v=VVc6pY7njCA:
+- 'Kid Icarus: Uprising'
+https://www.youtube.com/watch?v=VVlFM_PDBmY:
+- Metal Gear Solid
+https://www.youtube.com/watch?v=VZIA6ZoLBEA:
+- Donkey Kong Country Returns
+https://www.youtube.com/watch?v=VdYkebbduH8:
+- Mario Kart 64
+https://www.youtube.com/watch?v=VfvadCcVXCo:
+- Crystal Beans from Dungeon Explorer
+https://www.youtube.com/watch?v=VgMHWxN2U_w:
+- Pokemon Trading Card Game
+https://www.youtube.com/watch?v=Vgxs785sqjw:
+- Persona 3 FES
+- Persona III FES
+https://www.youtube.com/watch?v=Vin5IrgdWnM:
+- Donkey Kong 64
+https://www.youtube.com/watch?v=VixvyNbhZ6E:
+- 'Lufia: The Legend Returns'
+https://www.youtube.com/watch?v=Vjf--bJDDGQ:
+- Super Mario 3D World
+https://www.youtube.com/watch?v=VktyN1crFLQ:
+- Devilish
+https://www.youtube.com/watch?v=Vl9Nz-X4M68:
+- Double Dragon Neon
+https://www.youtube.com/watch?v=VmOy8IvUcAE:
+- F-Zero GX
+https://www.youtube.com/watch?v=VmemS-mqlOQ:
+- Nostalgia
+https://www.youtube.com/watch?v=VpOYrLJKFUk:
+- The Walking Dead
+https://www.youtube.com/watch?v=VpyUtWCMrb4:
+- Castlevania III
+- Castlevania 3
+https://www.youtube.com/watch?v=VsvQy72iZZ8:
+- 'Kid Icarus: Uprising'
+https://www.youtube.com/watch?v=Vt2-826EsT8:
+- Cosmic Star Heroine
+https://www.youtube.com/watch?v=VuT5ukjMVFw:
+- Grandia III
+- Grandia 3
+https://www.youtube.com/watch?v=VvMkmsgHWMo:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=VxJf8k4YzBY:
+- The Mummy Demastered
+https://www.youtube.com/watch?v=VxgLPrbSg-U:
+- Romancing Saga 3
+- Romancing Saga III
+https://www.youtube.com/watch?v=VzHPc-HJlfM:
+- Tales of Symphonia
+https://www.youtube.com/watch?v=VzJ2MLvIGmM:
+- 'E.V.O.: Search for Eden'
+- 'E.5.O.: Search for Eden'
+https://www.youtube.com/watch?v=W0fvt7_n9CU:
+- 'Castlevania: Lords of Shadow'
+https://www.youtube.com/watch?v=W4259ddJDtw:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=W6GNcYfHe1E:
+- Illusion of Gaia
+https://www.youtube.com/watch?v=W6O1WLDxRrY:
+- Krater
+https://www.youtube.com/watch?v=W7RPY-oiDAQ:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=W8-GDfP2xNM:
+- Suikoden III
+- Suikoden 3
+https://www.youtube.com/watch?v=W8Y2EuSrz-k:
+- Sonic the Hedgehog 3
+- Sonic the Hedgehog III
+https://www.youtube.com/watch?v=WBawD9gcECk:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=WCGk_7V5IGk:
+- Super Street Fighter II
+- Super Street Fighter 2
+https://www.youtube.com/watch?v=WEoHCLNJPy0:
+- Radical Dreamers
+https://www.youtube.com/watch?v=WLorUNfzJy8:
+- Breath of Death VII
+- Breath of Death 7
+https://www.youtube.com/watch?v=WNORnKsigdQ:
+- Radiant Historia
+https://www.youtube.com/watch?v=WP9081WAmiY:
+- 'The Legend of Zelda: Wind Waker'
+- wind waker
+https://www.youtube.com/watch?v=WR_AncWskUk:
+- Metal Saga
+https://www.youtube.com/watch?v=WV56iJ9KFlw:
+- Mass Effect
+https://www.youtube.com/watch?v=WY2kHNPn-x8:
+- Tearaway
+https://www.youtube.com/watch?v=WYRFMUNIUVw:
+- Chrono Trigger
+https://www.youtube.com/watch?v=WZ1TQWlSOxo:
+- OutRun 2
+- OutRun II
+https://www.youtube.com/watch?v=W_t9udYAsBE:
+- Super Mario Bros 3
+- Super Mario Bros III
+https://www.youtube.com/watch?v=WaThErXvfD8:
+- Super Mario RPG
+https://www.youtube.com/watch?v=WcM38YKdk44:
+- Killer7
+https://www.youtube.com/watch?v=WdZPEL9zoMA:
+- Celeste
+https://www.youtube.com/watch?v=WeVAeMWeFNo:
+- 'Sakura Taisen: Atsuki Chishio Ni'
+https://www.youtube.com/watch?v=WgK4UTP0gfU:
+- Breath of Fire II
+- Breath of Fire 2
+https://www.youtube.com/watch?v=Wj17uoJLIV8:
+- Prince of Persia
+https://www.youtube.com/watch?v=Wqv5wxKDSIo:
+- Scott Pilgrim vs the World
+https://www.youtube.com/watch?v=WwXBfLnChSE:
+- Radiant Historia
+https://www.youtube.com/watch?v=WwuhhymZzgY:
+- The Last Story
+https://www.youtube.com/watch?v=X1-oxRS8-m4:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=X3rxfNjBGqQ:
+- Earthbound
+https://www.youtube.com/watch?v=X68AlSKY0d8:
+- Shatter
+https://www.youtube.com/watch?v=X80YQj6UHG8:
+- Xenogears
+https://www.youtube.com/watch?v=X8CGqt3N4GA:
+- 'The Legend of Zelda: Majora''s Mask'
+- 'majora''s mask'
+https://www.youtube.com/watch?v=XCfYHd-9Szw:
+- Mass Effect
+https://www.youtube.com/watch?v=XG7HmRvDb5Y:
+- Yoshi's Woolly World
+https://www.youtube.com/watch?v=XH1J5XxZklI:
+- Snowboard Kids
+https://www.youtube.com/watch?v=XIzflqDtA1M:
+- Lone Survivor
+https://www.youtube.com/watch?v=XJllrwZzWwc:
+- Namco x Capcom
+https://www.youtube.com/watch?v=XKI0-dPmwSo:
+- Shining Force II
+- Shining Force 2
+https://www.youtube.com/watch?v=XKeXXWBYTkE:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=XLJxqz83ujw:
+- Glover
+https://www.youtube.com/watch?v=XMc9xjrnySg:
+- Golden Sun
+https://www.youtube.com/watch?v=XSSNGYomwAU:
+- Suikoden
+https://www.youtube.com/watch?v=XSkuBJx8q-Q:
+- Doki Doki Literature Club!
+https://www.youtube.com/watch?v=XW3Buw2tUgI:
+- Extreme-G
+https://www.youtube.com/watch?v=XWd4539-gdk:
+- Tales of Phantasia
+https://www.youtube.com/watch?v=XZEuJnSFz9U:
+- Bubble Bobble
+https://www.youtube.com/watch?v=X_PszodM17s:
+- ICO
+https://www.youtube.com/watch?v=Xa7uyLoh8t4:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=Xb8k4cp_mvQ:
+- Sonic the Hedgehog 2
+- Sonic the Hedgehog II
+https://www.youtube.com/watch?v=XelC_ns-vro:
+- Breath of Fire II
+- Breath of Fire 2
+https://www.youtube.com/watch?v=XhlM0eFM8F4:
+- Banjo-Tooie
+https://www.youtube.com/watch?v=Xkr40w4TfZQ:
+- 'The Legend of Zelda: Skyward Sword'
+- 'skyward sword'
+https://www.youtube.com/watch?v=Xm7lW0uvFpc:
+- DuckTales
+https://www.youtube.com/watch?v=XmAMeRNX_D4:
+- Ragnarok Online
+https://www.youtube.com/watch?v=XmmV5c2fS30:
+- Final Fantasy Mystic Quest
+https://www.youtube.com/watch?v=XnHysmcf31o:
+- Mother
+https://www.youtube.com/watch?v=Xnmuncx1F0Q:
+- Demon's Crest
+https://www.youtube.com/watch?v=Xo1gsf_pmzM:
+- Super Mario 3D World
+https://www.youtube.com/watch?v=Xpwy4RtRA1M:
+- The Witcher
+https://www.youtube.com/watch?v=XqPsT01sZVU:
+- Kingdom of Paradise
+https://www.youtube.com/watch?v=Xv_VYdKzO3A:
+- 'Tintin: Prisoners of the Sun'
+https://www.youtube.com/watch?v=Xw58jPitU-Q:
+- Ys Origin
+https://www.youtube.com/watch?v=XxMf4BdVq_g:
+- Deus Ex
+https://www.youtube.com/watch?v=Xy9eA5PJ9cU:
+- Pokemon
+https://www.youtube.com/watch?v=XztQyuJ4HoQ:
+- Legend of Legaia
+https://www.youtube.com/watch?v=Y0oO0bOyIAU:
+- Hotline Miami
+https://www.youtube.com/watch?v=Y1i3z56CiU4:
+- Pokemon X / Y
+- pokemon x
+- pokemon y
+https://www.youtube.com/watch?v=Y5HHYuQi7cQ:
+- Castlevania Curse of Darkness
+https://www.youtube.com/watch?v=Y5cXKVt3wOE:
+- Souten no Celenaria
+https://www.youtube.com/watch?v=Y7McPnKoP8g:
+- Illusion of Gaia
+https://www.youtube.com/watch?v=Y8Z8C0kziMw:
+- Sonic the Hedgehog
+https://www.youtube.com/watch?v=Y9a5VahqzOE:
+- Kirby's Dream Land
+https://www.youtube.com/watch?v=YA3VczBNCh8:
+- Lost Odyssey
+https://www.youtube.com/watch?v=YADDsshr-NM:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=YCwOCGt97Y0:
+- Kaiser Knuckle
+https://www.youtube.com/watch?v=YD19UMTxu4I:
+- Time Trax
+https://www.youtube.com/watch?v=YEoAPCEZyA0:
+- Breath of Fire III
+- Breath of Fire 3
+https://www.youtube.com/watch?v=YFDcu-hy4ak:
+- Donkey Kong Country 3
+- Donkey Kong Country III
+https://www.youtube.com/watch?v=YFz1vqikCaE:
+- 'TMNT IV: Turtles in Time'
+- 'TMNT 4: Turtles in Time'
+https://www.youtube.com/watch?v=YJcuMHvodN4:
+- Super Bonk
+https://www.youtube.com/watch?v=YKe8k8P2FNw:
+- Baten Kaitos
+https://www.youtube.com/watch?v=YL5Q4GybKWc:
+- Balloon Fight
+https://www.youtube.com/watch?v=YQasQAYgbb4:
+- Terranigma
+https://www.youtube.com/watch?v=YYBmrB3bYo4:
+- Christmas NiGHTS
+https://www.youtube.com/watch?v=YYGKW8vyYXU:
+- Shatterhand
+https://www.youtube.com/watch?v=YYxvaixwybA:
+- Shatter
+https://www.youtube.com/watch?v=YZGrI4NI9Nw:
+- Guacamelee!
+https://www.youtube.com/watch?v=Y_GJywu5Wr0:
+- Final Fantasy Tactics A2
+https://www.youtube.com/watch?v=Y_RoEPwYLug:
+- Mega Man ZX
+https://www.youtube.com/watch?v=YdcgKnwaE-A:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=YfFxcLGBCdI:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=Yh0LHDiWJNk:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=Yh4e_rdWD-k:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=YhOUDccL1i8:
+- Rudra No Hihou
+https://www.youtube.com/watch?v=YlLX3U6QfyQ:
+- 'Albert Odyssey: Legend of Eldean'
+https://www.youtube.com/watch?v=YmF88zf5qhM:
+- Halo 4
+- Halo IV
+https://www.youtube.com/watch?v=YmaHBaNxWt0:
+- Tekken 7
+- Tekken VII
+https://www.youtube.com/watch?v=Yn9EmSHIaLw:
+- Stardew Valley
+https://www.youtube.com/watch?v=YnQ9nrcp_CE:
+- Skyblazer
+https://www.youtube.com/watch?v=YqPjWx9XiWk:
+- Captain America and the Avengers
+https://www.youtube.com/watch?v=Ys_xfruRWSc:
+- Pokemon Snap
+https://www.youtube.com/watch?v=Yx-m8z-cbzo:
+- Cave Story
+https://www.youtube.com/watch?v=YzELBO_3HzE:
+- Plok
+https://www.youtube.com/watch?v=Z-LAcjwV74M:
+- Soul Calibur II
+- Soul Calibur 2
+https://www.youtube.com/watch?v=Z167OL2CQJw:
+- Lost Eden
+https://www.youtube.com/watch?v=Z41vcQERnQQ:
+- Dragon Quest III
+- Dragon Quest 3
+https://www.youtube.com/watch?v=Z49Lxg65UOM:
+- Tsugunai
+https://www.youtube.com/watch?v=Z4QunenBEXI:
+- 'The Legend of Zelda: Link''s Awakening'
+- 'link''s awakening'
+https://www.youtube.com/watch?v=Z74e6bFr5EY:
+- Chrono Trigger
+https://www.youtube.com/watch?v=Z8Jf5aFCbEE:
+- Secret of Evermore
+https://www.youtube.com/watch?v=Z9UnlYHogTE:
+- The Violinist of Hameln
+https://www.youtube.com/watch?v=ZAyRic3ZW0Y:
+- Comix Zone
+https://www.youtube.com/watch?v=ZCd2Y1HlAnA:
+- 'Atelier Iris: Eternal Mana'
+https://www.youtube.com/watch?v=ZEUEQ4wlvi4:
+- Dragon Quest III
+- Dragon Quest 3
+https://www.youtube.com/watch?v=ZJGF0_ycDpU:
+- Pokemon GO
+https://www.youtube.com/watch?v=ZJjaiYyES4w:
+- 'Tales of Symphonia: Dawn of the New World'
+https://www.youtube.com/watch?v=ZQGc9rG5qKo:
+- Metroid Prime
+https://www.youtube.com/watch?v=ZW-eiSBpG8E:
+- Legend of Mana
+https://www.youtube.com/watch?v=ZabqQ7MSsIg:
+- Wrecking Crew
+https://www.youtube.com/watch?v=ZbIfD6r518s:
+- Secret of Mana
+https://www.youtube.com/watch?v=ZbPfNA8vxQY:
+- Dustforce
+https://www.youtube.com/watch?v=ZbpEhw42bvQ:
+- Mega Man 6
+- Mega Man VI
+https://www.youtube.com/watch?v=Zee9VKBU_Vk:
+- Fallout 3
+- Fallout III
+https://www.youtube.com/watch?v=ZfZQWz0VVxI:
+- Platoon
+https://www.youtube.com/watch?v=ZgvsIvHJTds:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=Zj3P44pqM_Q:
+- Final Fantasy XI
+- Final Fantasy 11
+https://www.youtube.com/watch?v=Zn8GP0TifCc:
+- Lufia II
+- Lufia 2
+https://www.youtube.com/watch?v=ZrDAjeoPR7A:
+- Child of Eden
+https://www.youtube.com/watch?v=ZriKAVSIQa0:
+- 'The Legend of Zelda: A Link Between Worlds'
+- a link between worlds
+https://www.youtube.com/watch?v=ZuM618JZgww:
+- Metroid Prime Hunters
+https://www.youtube.com/watch?v=ZulAUy2_mZ4:
+- Gears of War 4
+- Gears of War IV
+https://www.youtube.com/watch?v=ZyAIAKItmoM:
+- Hotline Miami 2
+- Hotline Miami II
+https://www.youtube.com/watch?v=Zys-MeBfBto:
+- Tales of Symphonia
+https://www.youtube.com/watch?v=_1CWWL9UBUk:
+- Final Fantasy V
+- Final Fantasy 5
+https://www.youtube.com/watch?v=_1rwSdxY7_g:
+- Breath of Fire III
+- Breath of Fire 3
+https://www.youtube.com/watch?v=_24ZkPUOIeo:
+- Pilotwings 64
+https://www.youtube.com/watch?v=_2FYWCCZrNs:
+- Chrono Cross
+https://www.youtube.com/watch?v=_3Am7OPTsSk:
+- Earthbound
+https://www.youtube.com/watch?v=_9LUtb1MOSU:
+- Secret of Mana
+https://www.youtube.com/watch?v=_BdvaCCUsYo:
+- Tales of Destiny
+https://www.youtube.com/watch?v=_CB18Elh4Rc:
+- Chrono Cross
+https://www.youtube.com/watch?v=_C_fXeDZHKw:
+- Secret of Evermore
+https://www.youtube.com/watch?v=_CeQp-NVkSw:
+- Grounseed
+https://www.youtube.com/watch?v=_EYg1z-ZmUs:
+- Breath of Death VII
+- Breath of Death 7
+https://www.youtube.com/watch?v=_Gnu2AttTPI:
+- Pokemon Black / White
+- pokemon black
+- pokemon white
+https://www.youtube.com/watch?v=_H42_mzLMz0:
+- Dragon Quest IV
+- Dragon Quest 4
+https://www.youtube.com/watch?v=_Ju6JostT7c:
+- Castlevania
+https://www.youtube.com/watch?v=_L6scVxzIiI:
+- 'The Legend of Zelda: Link''s Awakening'
+- 'link''s awakening'
+https://www.youtube.com/watch?v=_Ms2ME7ufis:
+- 'Superbrothers: Sword & Sworcery EP'
+- 'superbrothers: sword and sworcery ep'
+https://www.youtube.com/watch?v=_OM5A6JwHig:
+- Machinarium
+https://www.youtube.com/watch?v=_OguBY5x-Qo:
+- Shenmue
+https://www.youtube.com/watch?v=_RHmWJyCsAM:
+- Dragon Quest II
+- Dragon Quest 2
+https://www.youtube.com/watch?v=_U3JUtO8a9U:
+- Mario + Rabbids Kingdom Battle
+https://www.youtube.com/watch?v=_Wcte_8oFyM:
+- Plants vs Zombies
+https://www.youtube.com/watch?v=_WjOqJ4LbkQ:
+- Mega Man 10
+https://www.youtube.com/watch?v=_XJw072Co_A:
+- Diddy Kong Racing
+https://www.youtube.com/watch?v=_YxsxsaP2T4:
+- 'The Elder Scrolls V: Skyrim'
+- 'The Elder Scrolls 5: Skyrim'
+- Skyrim
+https://www.youtube.com/watch?v=_bOxB__fyJI:
+- World Reborn
+https://www.youtube.com/watch?v=_blDkW4rCwc:
+- Hyrule Warriors
+https://www.youtube.com/watch?v=_cglnkygG_0:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=_dXaKTGvaEk:
+- 'Fatal Frame II: Crimson Butterfly'
+- 'Fatal Frame 2: Crimson Butterfly'
+https://www.youtube.com/watch?v=_dsKphN-mEI:
+- Sonic Unleashed
+https://www.youtube.com/watch?v=_eDz4_fCerk:
+- Jurassic Park
+https://www.youtube.com/watch?v=_gmeGnmyn34:
+- 3D Dot Game Heroes
+https://www.youtube.com/watch?v=_hRi2AwnEyw:
+- Dragon Quest V
+- Dragon Quest 5
+https://www.youtube.com/watch?v=_i9LIgKpgkc:
+- Persona 3
+- Persona III
+https://www.youtube.com/watch?v=_j8AXugAZCQ:
+- Earthbound
+https://www.youtube.com/watch?v=_jWbBWpfBWw:
+- Advance Wars
+https://www.youtube.com/watch?v=_joPG7N0lRk:
+- Lufia II
+- Lufia 2
+https://www.youtube.com/watch?v=_qbSmANSx6s:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=_thDGKkVgIE:
+- 'Spyro: A Hero''s Tail'
+https://www.youtube.com/watch?v=_ttw1JCEiZE:
+- NieR
+https://www.youtube.com/watch?v=_wHwJoxw4i4:
+- Metroid
+https://www.youtube.com/watch?v=_wbGNLXgYgE:
+- Grand Theft Auto V
+- Grand Theft Auto 5
+https://www.youtube.com/watch?v=a0oq7Yw8tkc:
+- 'The Binding of Isaac: Rebirth'
+https://www.youtube.com/watch?v=a14tqUAswZU:
+- Daytona USA
+https://www.youtube.com/watch?v=a43NXcUkHkI:
+- Final Fantasy
+https://www.youtube.com/watch?v=a4t1ty8U9qw:
+- Earthbound
+https://www.youtube.com/watch?v=a5JdLRzK_uQ:
+- Wild Arms 3
+- Wild Arms III
+https://www.youtube.com/watch?v=a5WtWa8lL7Y:
+- Tetris
+https://www.youtube.com/watch?v=a8hAxP__AKw:
+- Lethal Weapon
+https://www.youtube.com/watch?v=a9MLBjUvgFE:
+- Minecraft (Update Aquatic)
+- Minecraft
+https://www.youtube.com/watch?v=aBmqRgtqOgw:
+- 'The Legend of Zelda: Minish Cap'
+- minish cap
+https://www.youtube.com/watch?v=aDJ3bdD4TPM:
+- Terranigma
+https://www.youtube.com/watch?v=aDbohXp2oEs:
+- Baten Kaitos Origins
+https://www.youtube.com/watch?v=aKgSFxA0ZhY:
+- Touch My Katamari
+https://www.youtube.com/watch?v=aKqYLGaG_E4:
+- Earthbound
+https://www.youtube.com/watch?v=aObuQqkoa-g:
+- Dragon Quest VI
+- Dragon Quest 6
+https://www.youtube.com/watch?v=aOjeeAVojAE:
+- Metroid AM2R
+https://www.youtube.com/watch?v=aOxqL6hSt8c:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=aPrcJy-5hoA:
+- 'Westerado: Double Barreled'
+https://www.youtube.com/watch?v=aRloSB3iXG0:
+- Metal Warriors
+https://www.youtube.com/watch?v=aTofARLXiBk:
+- Jazz Jackrabbit 3
+- Jazz Jackrabbit III
+https://www.youtube.com/watch?v=aU0WdpQRzd4:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=aWh7crjCWlM:
+- Conker's Bad Fur Day
+https://www.youtube.com/watch?v=aXJ0om-_1Ew:
+- Crystal Beans from Dungeon Explorer
+https://www.youtube.com/watch?v=aYUMd2GvwsU:
+- A Bug's Life
+https://www.youtube.com/watch?v=aZ37adgwDIw:
+- Shenmue
+https://www.youtube.com/watch?v=a_WurTZJrpE:
+- Earthbound
+https://www.youtube.com/watch?v=a_qDMzn6BOA:
+- Shin Megami Tensei IV
+- Shin Megami Tensei 4
+https://www.youtube.com/watch?v=aatRnG3Tkmg:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=abv-zluKyQQ:
+- Final Fantasy Tactics Advance
+https://www.youtube.com/watch?v=acAAz1MR_ZA:
+- 'Disgaea: Hour of Darkness'
+https://www.youtube.com/watch?v=acLncvJ9wHI:
+- Trauma Team
+https://www.youtube.com/watch?v=acVjEoRvpv8:
+- Dark Cloud
+https://www.youtube.com/watch?v=aci_luVBju4:
+- 'The Legend of Zelda: Skyward Sword'
+- skyward sword
+https://www.youtube.com/watch?v=ae_lrtPgor0:
+- Super Mario World
+https://www.youtube.com/watch?v=afsUV7q6Hqc:
+- Mega Man ZX
+https://www.youtube.com/watch?v=ag5q7vmDOls:
+- Lagoon
+https://www.youtube.com/watch?v=aj9mW0Hvp0g:
+- 'Ufouria: The Saga'
+https://www.youtube.com/watch?v=am5TVpGwHdE:
+- Digital Devil Saga
+https://www.youtube.com/watch?v=ammnaFhcafI:
+- Mario Party 4
+- Mario Party IV
+https://www.youtube.com/watch?v=an3P8otlD2A:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=aqLjvjhHgDI:
+- Minecraft
+https://www.youtube.com/watch?v=aqWw9gLgFRA:
+- Portal
+https://www.youtube.com/watch?v=aumWblPK58M:
+- SaGa Frontier
+https://www.youtube.com/watch?v=avyGawaBrtQ:
+- 'Dragon Ball Z: Budokai'
+https://www.youtube.com/watch?v=b-oxtWJ00WA:
+- Pikmin 3
+- Pikmin III
+https://www.youtube.com/watch?v=b-rgxR_zIC4:
+- Mega Man 4
+- Mega Man IV
+https://www.youtube.com/watch?v=b0kqwEbkSag:
+- Deathsmiles IIX
+https://www.youtube.com/watch?v=b1YKRCKnge8:
+- Chrono Trigger
+https://www.youtube.com/watch?v=b3Ayzzo8eZo:
+- 'Lunar: Dragon Song'
+https://www.youtube.com/watch?v=b3l5v-QQF40:
+- 'TMNT IV: Turtles in Time'
+- 'TMNT 4: Turtles in Time'
+https://www.youtube.com/watch?v=b6QzJaltmUM:
+- What Remains of Edith Finch
+https://www.youtube.com/watch?v=b9OZwTLtrl4:
+- Legend of Mana
+https://www.youtube.com/watch?v=bA4PAkrAVpQ:
+- Mega Man 9
+- Mega Man IX
+https://www.youtube.com/watch?v=bAkK3HqzoY0:
+- 'Fire Emblem: Radiant Dawn'
+https://www.youtube.com/watch?v=bCNdNTdJYvE:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=bDH8AIok0IM:
+- TimeSplitters 2
+- TimeSplitters II
+https://www.youtube.com/watch?v=bFk3mS6VVsE:
+- Threads of Fate
+https://www.youtube.com/watch?v=bItjdi5wxFQ:
+- Watch Dogs
+https://www.youtube.com/watch?v=bKj3JXmYDfY:
+- The Swapper
+https://www.youtube.com/watch?v=bNzYIEY-CcM:
+- Chrono Trigger
+https://www.youtube.com/watch?v=bO2wTaoCguc:
+- Super Dodge Ball
+https://www.youtube.com/watch?v=bOg8XuvcyTY:
+- Final Fantasy Legend II
+- Final Fantasy Legend 2
+https://www.youtube.com/watch?v=bQ1D8oR128E:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=bR4AB3xNT0c:
+- Donkey Kong Country Returns
+https://www.youtube.com/watch?v=bRAT5LgAl5E:
+- The Dark Spire
+https://www.youtube.com/watch?v=bW3KNnZ2ZiA:
+- Chrono Trigger
+https://www.youtube.com/watch?v=bWp4F1p2I64:
+- 'Deus Ex: Human Revolution'
+https://www.youtube.com/watch?v=bXfaBUCDX1I:
+- 'Mario & Luigi: Dream Team'
+- 'mario and luigi: dream team'
+https://www.youtube.com/watch?v=bZBoTinEpao:
+- Jade Cocoon
+https://www.youtube.com/watch?v=bcHL3tGy7ws:
+- World of Warcraft
+https://www.youtube.com/watch?v=bckgyhCo7Lw:
+- Tales of Legendia
+https://www.youtube.com/watch?v=bdNrjSswl78:
+- Kingdom Hearts II
+- Kingdom Hearts 2
+https://www.youtube.com/watch?v=berZX7mPxbE:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=bffwco66Vnc:
+- 'Little Nemo: The Dream Master'
+https://www.youtube.com/watch?v=bhW8jNscYqA:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=bmsw4ND8HNA:
+- Pokemon Diamond / Pearl / Platinum
+- pokemon diamond
+- pokemon pearl
+- pokemon platinum
+https://www.youtube.com/watch?v=bp4-fXuTwb8:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=brYzVFvM98I:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=bss8vzkIB74:
+- 'Vampire The Masquerade: Bloodlines'
+https://www.youtube.com/watch?v=bu-kSDUXUts:
+- Silent Hill 2
+- Silent Hill II
+https://www.youtube.com/watch?v=bvbOS8Mp8aQ:
+- Street Fighter V
+- Street Fighter 5
+https://www.youtube.com/watch?v=bviyWo7xpu0:
+- Wild Arms 5
+- Wild Arms V
+https://www.youtube.com/watch?v=c2Y1ANec-5M:
+- Ys Chronicles
+https://www.youtube.com/watch?v=c47-Y-y_dqI:
+- Blue Dragon
+https://www.youtube.com/watch?v=c62hLhyF2D4:
+- Jelly Defense
+https://www.youtube.com/watch?v=c7mYaBoSIQU:
+- Contact
+https://www.youtube.com/watch?v=c8sDG4L-qrY:
+- Skullgirls
+https://www.youtube.com/watch?v=cETUoqcjICE:
+- Vay
+https://www.youtube.com/watch?v=cHfgcOHSTs0:
+- Ogre Battle 64
+https://www.youtube.com/watch?v=cKBgNT-8rrM:
+- Grounseed
+https://www.youtube.com/watch?v=cKQZVFIuyko:
+- Streets of Rage 2
+- Streets of Rage II
+https://www.youtube.com/watch?v=cMxOAeESteU:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=cO1UTkT6lf8:
+- Baten Kaitos Origins
+https://www.youtube.com/watch?v=cQhqxEIAZbE:
+- Resident Evil Outbreak
+https://www.youtube.com/watch?v=cRyIPt01AVM:
+- Banjo-Kazooie
+https://www.youtube.com/watch?v=cU1Z5UwBlQo:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=cWTZEXmWcOs:
+- Night in the Woods
+https://www.youtube.com/watch?v=cWt6j5ZJCHU:
+- R-Type Delta
+https://www.youtube.com/watch?v=cYV7Ph-qvvI:
+- 'Romancing Saga: Minstrel Song'
+https://www.youtube.com/watch?v=cYlKsL8r074:
+- NieR
+https://www.youtube.com/watch?v=cZVRDjJUPIQ:
+- Kirby & The Rainbow Curse
+- kirby and the rainbow curse
+https://www.youtube.com/watch?v=c_ex2h9t2yk:
+- Klonoa
+https://www.youtube.com/watch?v=calW24ddgOM:
+- 'Castlevania: Order of Ecclesia'
+https://www.youtube.com/watch?v=cbiEH5DMx78:
+- Wild Arms
+https://www.youtube.com/watch?v=ccHauz5l5Kc:
+- Torchlight
+https://www.youtube.com/watch?v=ccMkXEV0YmY:
+- Tekken 2
+- Tekken II
+https://www.youtube.com/watch?v=ciM3PBf1DRs:
+- Sonic the Hedgehog
+https://www.youtube.com/watch?v=cjrh4YcvptY:
+- Jurassic Park 2
+- Jurassic Park II
+https://www.youtube.com/watch?v=ckVmgiTobAw:
+- Okami
+https://www.youtube.com/watch?v=cl6iryREksM:
+- 'The Legend of Zelda: Skyward Sword'
+- skyward sword
+https://www.youtube.com/watch?v=clyy2eKqdC0:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=cmyK3FdTu_Q:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=cnjADMWesKk:
+- Silent Hill 2
+- Silent Hill II
+https://www.youtube.com/watch?v=coyl_h4_tjc:
+- Mega Man 2
+- Mega Man II
+https://www.youtube.com/watch?v=cpcx0UQt4Y8:
+- Shadow of the Beast
+https://www.youtube.com/watch?v=cqSEDRNwkt8:
+- 'SMT: Digital Devil Saga 2'
+- 'SMT: Digital Devil Saga II'
+https://www.youtube.com/watch?v=cqkYQt8dnxU:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=cs3hwrowdaQ:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=cvae_OsnWZ0:
+- Super Mario RPG
+https://www.youtube.com/watch?v=cvpGCTUGi8o:
+- Pokemon Diamond / Pearl / Platinum
+- pokemon diamond
+- pokemon pearl
+- pokemon platinum
+https://www.youtube.com/watch?v=cwmHupo9oSQ:
+- Street Fighter 2010
+- Street Fighter MMX
+https://www.youtube.com/watch?v=cxAE48Dul7Y:
+- Top Gear 3000
+- Top Gear MMM
+https://www.youtube.com/watch?v=cxAbbHCpucs:
+- Unreal Tournament
+https://www.youtube.com/watch?v=cyShVri-4kQ:
+- NieR
+https://www.youtube.com/watch?v=d0akzKhBl2k:
+- Pop'n Music 7
+- Pop'n Music VII
+https://www.youtube.com/watch?v=d12Pt-zjLsI:
+- Fire Emblem Fates
+https://www.youtube.com/watch?v=d1UyVXN13SI:
+- 'The Legend of Zelda: A Link to the Past'
+- a link to the past
+https://www.youtube.com/watch?v=d2dB0PuWotU:
+- Sonic Adventure 2
+- Sonic Adventure II
+https://www.youtube.com/watch?v=d3mJsXFGhDg:
+- Altered Beast
+https://www.youtube.com/watch?v=d5OK1GkI_CU:
+- Chrono Cross
+https://www.youtube.com/watch?v=d8xtkry1wK8:
+- Lost Odyssey
+https://www.youtube.com/watch?v=dBsdllfE4Ek:
+- Undertale
+https://www.youtube.com/watch?v=dEVI5_OxUyY:
+- Goldeneye
+https://www.youtube.com/watch?v=dG4ZCzodq4g:
+- Tekken 6
+- Tekken VI
+https://www.youtube.com/watch?v=dGF7xsF0DmQ:
+- Shatterhand (JP)
+- shatterhand
+https://www.youtube.com/watch?v=dGzGSapPGL8:
+- Jets 'N' Guns
+https://www.youtube.com/watch?v=dJzTqmQ_erE:
+- Chrono Trigger
+https://www.youtube.com/watch?v=dMSjvBILQRU:
+- Fable
+https://www.youtube.com/watch?v=dMYW4wBDQLU:
+- 'Ys VI: The Ark of Napishtim'
+- 'Ys 6: The Ark of Napishtim'
+https://www.youtube.com/watch?v=dO4awKzd8rc:
+- One Step Beyond
+https://www.youtube.com/watch?v=dOHur-Yc3nE:
+- Shatter
+https://www.youtube.com/watch?v=dQRiJz_nEP8:
+- Castlevania II
+- Castlevania 2
+https://www.youtube.com/watch?v=dRHpQFbEthY:
+- Shovel Knight
+https://www.youtube.com/watch?v=dSwUFI18s7s:
+- Valkyria Chronicles 3
+- Valkyria Chronicles III
+https://www.youtube.com/watch?v=dTjNEOT-e-M:
+https://www.youtube.com/watch?v=dUcTukA0q4Y:
+- Super Mario World
+- 'FTL: Faster Than Light'
+https://www.youtube.com/watch?v=dWiHtzP-bc4:
+- 'Kirby 64: The Crystal Shards'
+https://www.youtube.com/watch?v=dWrm-lwiKj0:
+- Nora to Toki no Koubou
+https://www.youtube.com/watch?v=dZAOgvdXhuk:
+- 'Star Wars: Shadows of the Empire'
+https://www.youtube.com/watch?v=dcEXzNbn20k:
+- Dead Space
+https://www.youtube.com/watch?v=dd2dbckq54Q:
+- Black/Matrix
+https://www.youtube.com/watch?v=deKo_UHZa14:
+- Return All Robots!
+https://www.youtube.com/watch?v=dfykPUgPns8:
+- Parasite Eve
+https://www.youtube.com/watch?v=dgD5lgqNzP8:
+- Dark Souls
+https://www.youtube.com/watch?v=dhGMZwQr0Iw:
+- Mario Kart 8
+- Mario Kart VIII
+https://www.youtube.com/watch?v=dhzrumwtwFY:
+- Final Fantasy Tactics
+https://www.youtube.com/watch?v=dim1KXcN34U:
+- ActRaiser
+https://www.youtube.com/watch?v=dj0Ib2lJ7JA:
+- Final Fantasy XII
+- Final Fantasy 12
+https://www.youtube.com/watch?v=dj8Qe3GUrdc:
+- 'Divinity II: Ego Draconis'
+- 'Divinity 2: Ego Draconis'
+https://www.youtube.com/watch?v=dlZyjOv5G80:
+- Shenmue
+https://www.youtube.com/watch?v=dobKarKesA0:
+- Elemental Master
+https://www.youtube.com/watch?v=dqww-xq7b9k:
+- SaGa Frontier II
+- SaGa Frontier 2
+https://www.youtube.com/watch?v=drFZ1-6r7W8:
+- Shenmue II
+- Shenmue 2
+https://www.youtube.com/watch?v=dszJhqoPRf8:
+- Super Mario Kart
+https://www.youtube.com/watch?v=dtITnB_uRzc:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=dyFj_MfRg3I:
+- Dragon Quest
+https://www.youtube.com/watch?v=dylldXUC20U:
+- 'The Legend of Zelda: Wind Waker'
+- wind waker
+https://www.youtube.com/watch?v=e-r3yVxzwe0:
+- Chrono Cross
+https://www.youtube.com/watch?v=e1HWSPwGlpA:
+- Doom 3
+- Doom III
+https://www.youtube.com/watch?v=e6cvikWAAEo:
+- Super Smash Bros. Brawl
+https://www.youtube.com/watch?v=e7YW5tmlsLo:
+- Cannon Fodder
+https://www.youtube.com/watch?v=e9uvCvvlMn4:
+- Wave Race 64
+https://www.youtube.com/watch?v=e9xHOWHjYKc:
+- Beyond Good & Evil
+- beyond good and evil
+https://www.youtube.com/watch?v=eCS1Tzbcbro:
+- Demon's Crest
+https://www.youtube.com/watch?v=eDOCPzvn87s:
+- Super Mario World
+https://www.youtube.com/watch?v=eDjJq3DsNTc:
+- Ar nosurge
+https://www.youtube.com/watch?v=eEZLBWjQsGk:
+- Parasite Eve
+https://www.youtube.com/watch?v=eF03eqpRxHE:
+- Soul Edge
+https://www.youtube.com/watch?v=eFN9fNhjRPg:
+- 'NieR: Automata'
+https://www.youtube.com/watch?v=eFR7iBDJYpI:
+- Ratchet & Clank
+- ratchet and clank
+https://www.youtube.com/watch?v=eKiz8PrTvSs:
+- Metroid
+https://www.youtube.com/watch?v=eLLdU3Td1w0:
+- Star Fox 64
+https://www.youtube.com/watch?v=eNXj--eakKg:
+- Chrono Trigger
+https://www.youtube.com/watch?v=eNXv3L_ebrk:
+- 'Moon: Remix RPG Adventure'
+https://www.youtube.com/watch?v=eOx1HJJ-Y8M:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=eRuhYeSR19Y:
+- Uncharted Waters
+https://www.youtube.com/watch?v=eRzo1UGPn9s:
+- 'TMNT IV: Turtles in Time'
+- 'TMNT 4: Turtles in Time'
+https://www.youtube.com/watch?v=eWsYdciDkqY:
+- Jade Cocoon
+https://www.youtube.com/watch?v=ehNS3sKQseY:
+- Wipeout Pulse
+https://www.youtube.com/watch?v=ehxzly2ogW4:
+- Xenoblade Chronicles X
+- Xenoblade Chronicles 10
+https://www.youtube.com/watch?v=ej4PiY8AESE:
+- 'Lunar: Eternal Blue'
+https://www.youtube.com/watch?v=eje3VwPYdBM:
+- Pop'n Music 7
+- Pop'n Music VII
+https://www.youtube.com/watch?v=elvSWFGFOQs:
+- Ridge Racer Type 4
+- Ridge Racer Type IV
+https://www.youtube.com/watch?v=emGEYMPc9sY:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=eoPtQd6adrA:
+- Talesweaver
+https://www.youtube.com/watch?v=euk6wHmRBNY:
+- Infinite Undiscovery
+https://www.youtube.com/watch?v=ev9G_jTIA-k:
+- Robotrek
+https://www.youtube.com/watch?v=evHQZjhE9CM:
+- The Bouncer
+https://www.youtube.com/watch?v=evVH7gshLs8:
+- Turok 2 (Gameboy)
+- Turok II
+- turok 2
+https://www.youtube.com/watch?v=eyhLabJvb2M:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=eyiABstbKJE:
+- Kirby's Return to Dreamland
+https://www.youtube.com/watch?v=f0UzNWcwC30:
+- Tales of Graces
+https://www.youtube.com/watch?v=f0bj_Aqhbb8:
+- Guild Wars 2
+- Guild Wars II
+https://www.youtube.com/watch?v=f0muXjuV6cc:
+- Super Mario World
+https://www.youtube.com/watch?v=f1EFHMwKdkY:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=f1QLfSOUiHU:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=f2XcqUwycvA:
+- 'Kingdom Hearts: Birth By Sleep'
+https://www.youtube.com/watch?v=f2q9axKQFHc:
+- Tekken Tag Tournament 2
+- Tekken Tag Tournament II
+https://www.youtube.com/watch?v=f3z73Xp9fCk:
+- Outlaws
+https://www.youtube.com/watch?v=f6QCNRRA1x0:
+- Legend of Mana
+https://www.youtube.com/watch?v=fEfuvS-V9PI:
+- Mii Channel
+https://www.youtube.com/watch?v=fH-lLbHbG-A:
+- 'TMNT IV: Turtles in Time'
+- 'TMNT 4: Turtles in Time'
+https://www.youtube.com/watch?v=fH66CHAUcoA:
+- Chrono Trigger
+https://www.youtube.com/watch?v=fJZoDK-N6ug:
+- Castlevania 64
+https://www.youtube.com/watch?v=fJkpSSJUxC4:
+- Castlevania Curse of Darkness
+https://www.youtube.com/watch?v=fNBMeTJb9SM:
+- Dead Rising 3
+- Dead Rising III
+https://www.youtube.com/watch?v=fRy6Ly5A5EA:
+- Legend of Dragoon
+https://www.youtube.com/watch?v=fTj73xQg2TY:
+- Child of Light
+https://www.youtube.com/watch?v=fTvPg89TIMI:
+- Tales of Legendia
+https://www.youtube.com/watch?v=fWqvxC_8yDk:
+- 'Ecco: The Tides of Time (Sega CD)'
+- 'Ecco: The Tides of Time'
+https://www.youtube.com/watch?v=fWx4q8GqZeo:
+- Digital Devil Saga
+https://www.youtube.com/watch?v=fXxbFMtx0Bo:
+- Halo 3 ODST
+- Halo III ODST
+https://www.youtube.com/watch?v=fYvGx-PEAtg:
+- Tales of Symphonia
+https://www.youtube.com/watch?v=f_UurCb4AD4:
+- Dewy's Adventure
+https://www.youtube.com/watch?v=fbc17iYI7PA:
+- Warcraft II
+- Warcraft 2
+https://www.youtube.com/watch?v=fcJLdQZ4F8o:
+- Ar Tonelico
+https://www.youtube.com/watch?v=fc_3fMMaWK8:
+- Secret of Mana
+https://www.youtube.com/watch?v=fd6QnwqipcE:
+- Mutant Mudds
+https://www.youtube.com/watch?v=feZJtZnevAM:
+- Pandora's Tower
+https://www.youtube.com/watch?v=fexAY_t4N8U:
+- Conker's Bad Fur Day
+https://www.youtube.com/watch?v=fg1PDaOnU2Q:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=fiPxE3P2Qho:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=fjNJqcuFD-A:
+- Katamari Damacy
+https://www.youtube.com/watch?v=fpVag5b7zHo:
+- Pokemon Omega Ruby / Alpha Sapphire
+- pokemon omega ruby
+- pokemon alpha sapphire
+https://www.youtube.com/watch?v=ft5DP1h8jsg:
+- Wild Arms 2
+- Wild Arms II
+https://www.youtube.com/watch?v=g2S2Lxzow3Q:
+- Mega Man 5
+- Mega Man V
+https://www.youtube.com/watch?v=g4Bnot1yBJA:
+- 'The Elder Scrolls III: Morrowind'
+- 'The Elder Scrolls 3: Morrowind'
+- Morrowind
+https://www.youtube.com/watch?v=g6vc_zFeHFk:
+- Streets of Rage
+https://www.youtube.com/watch?v=gAy6qk8rl5I:
+- Wild Arms
+https://www.youtube.com/watch?v=gDL6uizljVk:
+- 'Batman: Return of the Joker'
+https://www.youtube.com/watch?v=gF4pOYxzplw:
+- Xenogears
+https://www.youtube.com/watch?v=gIlGulCdwB4:
+- Pilotwings Resort
+https://www.youtube.com/watch?v=gLfz9w6jmJM:
+- Machinarium
+https://www.youtube.com/watch?v=gLu7Bh0lTPs:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=gQUe8l_Y28Y:
+- Lineage II
+- Lineage 2
+https://www.youtube.com/watch?v=gQiYZlxJk3w:
+- Lufia II
+- Lufia 2
+https://www.youtube.com/watch?v=gQndM8KdTD0:
+- 'Kirby 64: The Crystal Shards'
+https://www.youtube.com/watch?v=gRZFl-vt4w0:
+- Ratchet & Clank
+- ratchet and clank
+https://www.youtube.com/watch?v=gSLIlAVZ6Eo:
+- Tales of Vesperia
+https://www.youtube.com/watch?v=gTahA9hCxAg:
+- Undertale
+https://www.youtube.com/watch?v=gVGhVofDPb4:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=gW0DiDKWgWc:
+- Earthbound
+https://www.youtube.com/watch?v=gWZ2cqFr0Vo:
+- Chrono Cross
+https://www.youtube.com/watch?v=gY9jq9-1LTk:
+- Treasure Hunter G
+https://www.youtube.com/watch?v=g_Hsyo7lmQU:
+- Pictionary
+https://www.youtube.com/watch?v=gcm3ak-SLqM:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=gf3NerhyM_k:
+- Tales of Berseria
+https://www.youtube.com/watch?v=ggTedyRHx20:
+- 'Qbeh-1: The Atlas Cube'
+- 'Qbeh-I: The Atlas Cube'
+https://www.youtube.com/watch?v=ghe_tgQvWKQ:
+- Resident Evil REmake
+https://www.youtube.com/watch?v=ght6F5_jHQ0:
+- Mega Man 4
+- Mega Man IV
+https://www.youtube.com/watch?v=glFK5I0G2GE:
+- Sheep Raider
+https://www.youtube.com/watch?v=glcGXw3gS6Q:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=gmfBMy-h6Js:
+- NieR
+https://www.youtube.com/watch?v=go7JlCI5n5o:
+- Counter Strike
+https://www.youtube.com/watch?v=gokt9KTpf18:
+- Mario Kart 7
+- Mario Kart VII
+https://www.youtube.com/watch?v=grQkblTqSMs:
+- Earthbound
+https://www.youtube.com/watch?v=grmP-wEjYKw:
+- Okami
+https://www.youtube.com/watch?v=guff_k4b6cI:
+- Wild Arms
+https://www.youtube.com/watch?v=gwesq9ElVM4:
+- 'The Legend of Zelda: A Link Between Worlds'
+- a link between worlds
+https://www.youtube.com/watch?v=gxF__3CNrsU:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=gzl9oJstIgg:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=h0LDHLeL-mE:
+- Sonic Forces
+https://www.youtube.com/watch?v=h0ed9Kei7dw:
+- Biker Mice from Mars
+https://www.youtube.com/watch?v=h2AhfGXAPtk:
+- Deathsmiles
+https://www.youtube.com/watch?v=h4VF0mL35as:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=h5iAyksrXgc:
+- Yoshi's Story
+https://www.youtube.com/watch?v=h8Z73i0Z5kk:
+- Final Fantasy Mystic Quest
+https://www.youtube.com/watch?v=h8wD8Dmxr94:
+- 'Dragon Ball Z: The Legacy of Goku II'
+- 'Dragon Ball Z: The Legacy of Goku 2'
+https://www.youtube.com/watch?v=hB3mYnW-v4w:
+- 'Superbrothers: Sword & Sworcery EP'
+- 'superbrothers: sword and sworcery ep'
+https://www.youtube.com/watch?v=hBg-pnQpic4:
+- Pokemon Ruby / Sapphire / Emerald
+- pokemon ruby
+- pokemon sapphire
+- pokemon emerald
+https://www.youtube.com/watch?v=hELte7HgL2Y:
+- Chrono Trigger
+https://www.youtube.com/watch?v=hFgqnQLyqqE:
+- Sonic Lost World
+https://www.youtube.com/watch?v=hL7-cD9LDp0:
+- Glover
+https://www.youtube.com/watch?v=hLKBPvLNzng:
+- 'The Legend of Zelda: A Link to the Past'
+- a link to the past
+https://www.youtube.com/watch?v=hMd5T_RlE_o:
+- Super Mario World
+https://www.youtube.com/watch?v=hMoejZAOOUM:
+- Ys II Chronicles
+- Ys 2 Chronicles
+https://www.youtube.com/watch?v=hNCGAN-eyuc:
+- Undertale
+https://www.youtube.com/watch?v=hNOTJ-v8xnk:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=hT8FhGDS5qE:
+- Lufia II
+- Lufia 2
+https://www.youtube.com/watch?v=hUpjPQWKDpM:
+- Breath of Fire V
+- Breath of Fire 5
+https://www.youtube.com/watch?v=hV3Ktwm356M:
+- Killer Instinct
+https://www.youtube.com/watch?v=hYHMbcC08xA:
+- Mega Man 9
+- Mega Man IX
+https://www.youtube.com/watch?v=h_suLF-Qy5U:
+- Katamari Damacy
+https://www.youtube.com/watch?v=hb6Ny-4Pb7o:
+- Mana Khemia
+https://www.youtube.com/watch?v=he2oLG1Trtg:
+- 'Kirby: Triple Deluxe'
+https://www.youtube.com/watch?v=heD3Rzhrnaw:
+- Mega Man 2
+- Mega Man II
+https://www.youtube.com/watch?v=he_ECgg9YyU:
+- Mega Man
+https://www.youtube.com/watch?v=hlCHzEa9MRg:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=hlQ-DG9Jy3Y:
+- Pop'n Music 2
+- Pop'n Music II
+https://www.youtube.com/watch?v=hoOeroni32A:
+- Mutant Mudds
+https://www.youtube.com/watch?v=hqKfTvkVo1o:
+- Monster Hunter Tri
+https://www.youtube.com/watch?v=hrxseupEweU:
+- Unreal Tournament 2003 & 2004
+- unreal tournament 2003
+- unreal tournament 2004
+https://www.youtube.com/watch?v=hsPiGiZ2ks4:
+- SimCity 4
+- SimCity IV
+https://www.youtube.com/watch?v=hv2BL0v2tb4:
+- Phantasy Star Online
+https://www.youtube.com/watch?v=hxZTBl7Q5fs:
+- 'The Legend of Zelda: Link''s Awakening'
+- 'link''s awakening'
+https://www.youtube.com/watch?v=hyjJl59f_I0:
+- Star Fox Adventures
+https://www.youtube.com/watch?v=i-hcCtD_aB0:
+- Soma Bringer
+https://www.youtube.com/watch?v=i-v-bJhK5yc:
+- Undertale
+https://www.youtube.com/watch?v=i1GFclxeDIU:
+- Shadow Hearts
+https://www.youtube.com/watch?v=i1ZVtT5zdcI:
+- Secret of Mana
+https://www.youtube.com/watch?v=i2E9c0j0n4A:
+- Rad Racer II
+- Rad Racer 2
+https://www.youtube.com/watch?v=i49PlEN5k9I:
+- Soul Sacrifice
+https://www.youtube.com/watch?v=i8DTcUWfmws:
+- Guilty Gear Isuka
+https://www.youtube.com/watch?v=iA6xXR3pwIY:
+- Baten Kaitos
+https://www.youtube.com/watch?v=iDIbO2QefKo:
+- Final Fantasy V
+- Final Fantasy 5
+https://www.youtube.com/watch?v=iDVMfUFs_jo:
+- LOST CHILD
+https://www.youtube.com/watch?v=iFa5bIrsWb0:
+- 'The Legend of Zelda: Link''s Awakening'
+https://www.youtube.com/watch?v=iJS-PjSQMtw:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=iK-g9PXhXzM:
+- Radiant Historia
+https://www.youtube.com/watch?v=iMeBQBv2ACs:
+- Etrian Mystery Dungeon
+https://www.youtube.com/watch?v=iN3Jp55Db_A:
+- Maniac Mansion
+https://www.youtube.com/watch?v=iS98ggIHkRw:
+- Extreme-G
+https://www.youtube.com/watch?v=iSP-_hNQyYs:
+- Chrono Trigger
+https://www.youtube.com/watch?v=iTUBlKA5IfY:
+- Pokemon Art Academy
+https://www.youtube.com/watch?v=iV5Ae4lOWmk:
+- Super Paper Mario
+https://www.youtube.com/watch?v=iXDF9eHsmD4:
+- F-Zero
+https://www.youtube.com/watch?v=iZv19yJrZyo:
+- 'FTL: Advanced Edition'
+https://www.youtube.com/watch?v=idw1zFkySA0:
+- Boot Hill Heroes
+https://www.youtube.com/watch?v=ietzDT5lOpQ:
+- Braid
+https://www.youtube.com/watch?v=ifqmN14qJp8:
+- Minecraft
+https://www.youtube.com/watch?v=ifvxBt7tmA8:
+- Yoshi's Island
+https://www.youtube.com/watch?v=iga0Ed0BWGo:
+- Diablo III
+- Diablo 3
+https://www.youtube.com/watch?v=ihi7tI8Kaxc:
+- The Last Remnant
+https://www.youtube.com/watch?v=ijUwAWUS8ug:
+- Diablo II
+- Diablo 2
+https://www.youtube.com/watch?v=ilOYzbGwX7M:
+- Deja Vu
+https://www.youtube.com/watch?v=imK2k2YK36E:
+- Giftpia
+https://www.youtube.com/watch?v=in7TUvirruo:
+- Sonic Unleashed
+https://www.youtube.com/watch?v=injXmLzRcBI:
+- Blue Dragon
+https://www.youtube.com/watch?v=iohvqM6CGEU:
+- Mario Kart 64
+https://www.youtube.com/watch?v=irGCdR0rTM4:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=irQxobE5PU8:
+- Frozen Synapse
+https://www.youtube.com/watch?v=ivfEScAwMrE:
+- 'Paper Mario: Sticker Star'
+https://www.youtube.com/watch?v=iwemkM-vBmc:
+- Super Mario Sunshine
+https://www.youtube.com/watch?v=ixE9HlQv7v8:
+- Castle Crashers
+https://www.youtube.com/watch?v=j16ZcZf9Xz8:
+- Pokemon Silver / Gold / Crystal
+- pokemon silver
+- pokemon gold
+- pokemon crystal
+https://www.youtube.com/watch?v=j2zAq26hqd8:
+- 'Metroid Prime 2: Echoes'
+- 'Metroid Prime II: Echoes'
+https://www.youtube.com/watch?v=j4Zh9IFn_2U:
+- Etrian Odyssey Untold
+https://www.youtube.com/watch?v=j6i73HYUNPk:
+- Gauntlet III
+- Gauntlet 3
+https://www.youtube.com/watch?v=jANl59bNb60:
+- Breath of Fire III
+- Breath of Fire 3
+https://www.youtube.com/watch?v=jAQGCM-IyOE:
+- Xenosaga
+https://www.youtube.com/watch?v=jChHVPyd4-Y:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=jEmyzsFaiZ8:
+- Tomb Raider
+https://www.youtube.com/watch?v=jI2ltHB50KU:
+- Opoona
+https://www.youtube.com/watch?v=jJVTRXZXEIA:
+- 'Dust: An Elysian Tail'
+https://www.youtube.com/watch?v=jL57YsG1JJE:
+- Metroid
+https://www.youtube.com/watch?v=jLrqs_dvAGU:
+- Yoshi's Woolly World
+https://www.youtube.com/watch?v=jNoiUfwuuP8:
+- 'Kirby 64: The Crystal Shards'
+https://www.youtube.com/watch?v=jObg1aw9kzE:
+- 'Divinity 2: Ego Draconis'
+- 'Divinity II: Ego Draconis'
+https://www.youtube.com/watch?v=jP2CHO9yrl8:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=jRqXWj7TL5A:
+- Goldeneye
+https://www.youtube.com/watch?v=jTZEuazir4s:
+- Environmental Station Alpha
+https://www.youtube.com/watch?v=jWtiuVTe5kQ:
+- Equinox
+https://www.youtube.com/watch?v=jXGaW3dKaHs:
+- Mega Man 2
+- Mega Man II
+https://www.youtube.com/watch?v=jYFYsfEyi0c:
+- Ragnarok Online
+https://www.youtube.com/watch?v=j_8sYSOkwAg:
+- Professor Layton and the Curious Village
+https://www.youtube.com/watch?v=j_EH4xCh1_U:
+- Metroid Prime
+https://www.youtube.com/watch?v=jaG1I-7dYvY:
+- Earthbound
+https://www.youtube.com/watch?v=jfEZs-Ada78:
+- Jack Bros.
+https://www.youtube.com/watch?v=jgtKSnmM5oE:
+- Tetris
+https://www.youtube.com/watch?v=jhHfROLw4fA:
+- 'Donkey Kong Country: Tropical Freeze'
+https://www.youtube.com/watch?v=jhsNQ6r2fHE:
+- 3D Dot Game Heroes
+https://www.youtube.com/watch?v=jkWYvENgxwA:
+- Journey to Silius
+https://www.youtube.com/watch?v=jlFYSIyAGmA:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=jlcjrgSVkkc:
+- Mario Kart 8
+- Mario Kart VIII
+https://www.youtube.com/watch?v=jpghr0u8LCU:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=jv5_zzFZMtk:
+- Shadow of the Colossus
+https://www.youtube.com/watch?v=k09qvMpZYYo:
+- Secret of Mana
+https://www.youtube.com/watch?v=k0f4cCJqUbg:
+- Katamari Forever
+https://www.youtube.com/watch?v=k3m-_uCo-b8:
+- Mega Man
+https://www.youtube.com/watch?v=k4L8cq2vrlk:
+- Chrono Trigger
+https://www.youtube.com/watch?v=k4N3Go4wZCg:
+- 'Uncharted: Drake''s Fortune'
+https://www.youtube.com/watch?v=kA69u0-U-Vk:
+- MapleStory
+https://www.youtube.com/watch?v=kDssUvBiHFk:
+- Yoshi's Island
+https://www.youtube.com/watch?v=kEA-4iS0sco:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=kJRiZaexNno:
+- Unlimited Saga
+https://www.youtube.com/watch?v=kN-jdHNOq8w:
+- Super Mario 64
+https://www.youtube.com/watch?v=kNDB4L0D2wg:
+- 'Everquest: Planes of Power'
+https://www.youtube.com/watch?v=kNPz77g5Xyk:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=kNgI7N5k5Zo:
+- 'Atelier Iris 2: The Azoth of Destiny'
+- 'Atelier Iris II: The Azoth of Destiny'
+https://www.youtube.com/watch?v=kW63YiVf5I0:
+- Splatoon
+https://www.youtube.com/watch?v=kWRFPdhAFls:
+- 'Fire Emblem 4: Seisen no Keifu'
+- 'Fire Emblem IV: Seisen no Keifu'
+https://www.youtube.com/watch?v=khPWld3iA8g:
+- Max Payne 2
+- Max Payne II
+https://www.youtube.com/watch?v=ki_ralGwQN4:
+- Legend of Mana
+https://www.youtube.com/watch?v=kmkzuPrQHQM:
+- Yoshi's Island DS
+https://www.youtube.com/watch?v=koHO9vN6t4I:
+- Giftpia
+https://www.youtube.com/watch?v=krmNfjbfJUQ:
+- Midnight Resistance
+https://www.youtube.com/watch?v=ks0xlnvjwMo:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=ks74Hlce8yw:
+- Super Mario 3D World
+https://www.youtube.com/watch?v=ksq6wWbVsPY:
+- Tekken 2
+- Tekken II
+https://www.youtube.com/watch?v=ktnL6toFPCo:
+- PaRappa the Rapper
+https://www.youtube.com/watch?v=ku0pS3ko5CU:
+- 'The Legend of Zelda: Minish Cap'
+- minish cap
+https://www.youtube.com/watch?v=kx580yOvKxs:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=kyaC_jSV_fw:
+- Nostalgia
+https://www.youtube.com/watch?v=kzId-AbowC4:
+- Xenosaga III
+- Xenosaga 3
+https://www.youtube.com/watch?v=kzUYJAaiEvA:
+- ICO
+https://www.youtube.com/watch?v=l1O9XZupPJY:
+- Tomb Raider III
+- Tomb Raider 3
+https://www.youtube.com/watch?v=l1UCISJoDTU:
+- Xenoblade Chronicles 2
+- Xenoblade Chronicles II
+https://www.youtube.com/watch?v=l5VK4kR1YTw:
+- Banjo-Kazooie
+https://www.youtube.com/watch?v=l5WLVNhczjE:
+- 'Phoenix Wright: Justice for All'
+https://www.youtube.com/watch?v=lBEvtA4Uuwk:
+- Wild Arms
+https://www.youtube.com/watch?v=lFOBRmPK-Qs:
+- Pokemon
+https://www.youtube.com/watch?v=lJc9ajk9bXs:
+- Sonic the Hedgehog 2
+- Sonic the Hedgehog II
+https://www.youtube.com/watch?v=lLP6Y-1_P1g:
+- Asterix & Obelix
+- asterix and obelix
+https://www.youtube.com/watch?v=lLniW316mUk:
+- Suikoden III
+- Suikoden 3
+https://www.youtube.com/watch?v=lOaWT7Y7ZNo:
+- Mirror's Edge
+https://www.youtube.com/watch?v=lPFndohdCuI:
+- Super Mario RPG
+https://www.youtube.com/watch?v=lXBFPdRiZMs:
+- 'Dragon Ball Z: Super Butoden'
+https://www.youtube.com/watch?v=lZUgl5vm6tk:
+- Trip World
+https://www.youtube.com/watch?v=lb88VsHVDbw:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=lcOky3CKCa0:
+- Yoshi's Woolly World
+https://www.youtube.com/watch?v=lfudDzITiw8:
+- Opoona
+https://www.youtube.com/watch?v=lhVk4Q47cgQ:
+- Shadow of the Colossus
+https://www.youtube.com/watch?v=lmhxytynQOE:
+- Romancing Saga 3
+- Romancing Saga III
+https://www.youtube.com/watch?v=loh0SQ_SF2s:
+- Xenogears
+https://www.youtube.com/watch?v=lwUtHErD2Yo:
+- Furi
+https://www.youtube.com/watch?v=lyduqdKbGSw:
+- Create
+https://www.youtube.com/watch?v=lzhkFmiTB_8:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=m-VXBxd2pmo:
+- Asterix
+https://www.youtube.com/watch?v=m09KrtCgiCA:
+- Final Fantasy Origins
+- final fantasy origins
+https://www.youtube.com/watch?v=m2Vlxyd9Wjw:
+- Dragon Quest V
+- Dragon Quest 5
+https://www.youtube.com/watch?v=m2q8wtFHbyY:
+- 'La Pucelle: Tactics'
+https://www.youtube.com/watch?v=m4NfokfW3jw:
+- 'Dragon Ball Z: The Legacy of Goku II'
+- 'Dragon Ball Z: The Legacy of Goku 2'
+https://www.youtube.com/watch?v=m4uR39jNeGE:
+- Wild Arms 3
+- Wild Arms III
+https://www.youtube.com/watch?v=m57kb5d3wZ4:
+- Chrono Cross
+https://www.youtube.com/watch?v=m6HgGxxjyrU:
+- Tales of Symphonia
+https://www.youtube.com/watch?v=m9kEp_sNLJo:
+- Illusion of Gaia
+https://www.youtube.com/watch?v=mA2rTmfT1T8:
+- Valdis Story
+https://www.youtube.com/watch?v=mASkgOcUdOQ:
+- Final Fantasy V
+- Final Fantasy 5
+https://www.youtube.com/watch?v=mDw3F-Gt4bQ:
+- Knuckles Chaotix
+https://www.youtube.com/watch?v=mG1D80dMhKo:
+- Asterix
+https://www.youtube.com/watch?v=mG9BcQEApoI:
+- 'Castlevania: Dawn of Sorrow'
+https://www.youtube.com/watch?v=mHUE5GkAUXo:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=mJCRoXkJohc:
+- Bomberman 64
+https://www.youtube.com/watch?v=mNDaE4dD8dE:
+- Monster Rancher 4
+- Monster Rancher IV
+https://www.youtube.com/watch?v=mPhy1ylhj7E:
+- Earthbound
+https://www.youtube.com/watch?v=mRGdr6iahg8:
+- 'Kirby 64: The Crystal Shards'
+https://www.youtube.com/watch?v=mSXFiU0mqik:
+- Chrono Cross
+https://www.youtube.com/watch?v=mTnXMcxBwcE:
+- 'The Legend of Zelda: Ocarina of Time'
+- ocarina of time
+https://www.youtube.com/watch?v=mWJeicPtar0:
+- NieR
+https://www.youtube.com/watch?v=mX78VEVMSVo:
+- Arcana
+https://www.youtube.com/watch?v=m_kAJLsSGz8:
+- 'Shantae: Risky''s Revenge'
+https://www.youtube.com/watch?v=mbPpGeTPbjM:
+- Eternal Darkness
+https://www.youtube.com/watch?v=mfOHgEeuEHE:
+- Umineko no Naku Koro ni
+https://www.youtube.com/watch?v=mh9iibxyg14:
+- Super Mario RPG
+https://www.youtube.com/watch?v=minJMBk4V9g:
+- 'Star Trek: Deep Space Nine'
+https://www.youtube.com/watch?v=mkTkAkcj6mQ:
+- 'The Elder Scrolls IV: Oblivion'
+- 'The Elder Scrolls 4: Oblivion'
+- Oblivion
+https://www.youtube.com/watch?v=mm-nVnt8L0g:
+- Earthbound
+https://www.youtube.com/watch?v=mnPqUs4DZkI:
+- 'Turok: Dinosaur Hunter'
+https://www.youtube.com/watch?v=mngXThXnpwY:
+- Legendary Wings
+https://www.youtube.com/watch?v=moDNdAfZkww:
+- Minecraft
+https://www.youtube.com/watch?v=mpt-RXhdZzQ:
+- Mega Man X
+https://www.youtube.com/watch?v=mr1anFEQV9s:
+- Alundra
+https://www.youtube.com/watch?v=msEbmIgnaSI:
+- Breath of Fire IV
+- Breath of Fire 4
+https://www.youtube.com/watch?v=mvcctOvLAh4:
+- Donkey Kong Country
+https://www.youtube.com/watch?v=mwWcWgKjN5Y:
+- F.E.A.R.
+https://www.youtube.com/watch?v=mwdGO2vfAho:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=myZzE9AYI90:
+- Silent Hill 2
+- Silent Hill II
+https://www.youtube.com/watch?v=myjd1MnZx5Y:
+- Dragon Quest IX
+- Dragon Quest 9
+https://www.youtube.com/watch?v=mzFGgwKMOKw:
+- Super Mario Land 2
+- Super Mario Land II
+https://www.youtube.com/watch?v=n10VyIRJj58:
+- Xenosaga III
+- Xenosaga 3
+https://www.youtube.com/watch?v=n2CyG6S363M:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=n4Pun5BDH0g:
+- Okamiden
+https://www.youtube.com/watch?v=n5L0ZpcDsZw:
+- Lufia II
+- Lufia 2
+https://www.youtube.com/watch?v=n5eb_qUg5rY:
+- Jazz Jackrabbit 2
+- Jazz Jackrabbit II
+https://www.youtube.com/watch?v=n6f-bb8DZ_k:
+- Street Fighter II
+- Street Fighter 2
+https://www.youtube.com/watch?v=n9QNuhs__8s:
+- 'Magna Carta: Tears of Blood'
+https://www.youtube.com/watch?v=nBWjVglSVGk:
+- The Flintstones
+https://www.youtube.com/watch?v=nEBoB571s9w:
+- Asterix & Obelix
+- asterix and obelix
+https://www.youtube.com/watch?v=nFsoCfGij0Y:
+- 'Batman: Return of the Joker'
+https://www.youtube.com/watch?v=nH7ma8TPnE8:
+- The Legend of Zelda
+https://www.youtube.com/watch?v=nJN-xeA7ZJo:
+- Treasure Master
+https://www.youtube.com/watch?v=nJgwF3gw9Xg:
+- 'Zelda II: The Adventure of Link'
+- 'Zelda 2: The Adventure of Link'
+https://www.youtube.com/watch?v=nK-IjRF-hs4:
+- 'Spyro: A Hero''s Tail'
+https://www.youtube.com/watch?v=nL3YMZ-Br0o:
+- Child of Light
+https://www.youtube.com/watch?v=nL5Y2NmHn38:
+- Fallout 4
+- Fallout IV
+https://www.youtube.com/watch?v=nO3lPvYVxzs:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=nOeGX-O_QRU:
+- Top Gear
+https://www.youtube.com/watch?v=nQC4AYA14UU:
+- Battery Jam
+https://www.youtube.com/watch?v=nRw54IXvpE8:
+- Gitaroo Man
+https://www.youtube.com/watch?v=nUScyv5DcIo:
+- Super Stickman Golf 2
+- Super Stickman Golf II
+https://www.youtube.com/watch?v=nUbwvWQOOvU:
+- Metal Gear Solid 3
+- Metal Gear Solid III
+https://www.youtube.com/watch?v=nUiZp8hb42I:
+- Intelligent Qube
+https://www.youtube.com/watch?v=nY8JLHPaN4c:
+- 'Ape Escape: Million Monkeys'
+https://www.youtube.com/watch?v=naIUUMurT5U:
+- Lara Croft GO
+https://www.youtube.com/watch?v=nea0ze3wh6k:
+- Toy Story 2
+- Toy Story II
+https://www.youtube.com/watch?v=nesYhwViPkc:
+- 'The Legend of Zelda: Tri Force Heroes'
+- tri force heroes
+https://www.youtube.com/watch?v=ng442hwhhAw:
+- Super Mario Bros 3
+- Super Mario Bros III
+https://www.youtube.com/watch?v=nj2d8U-CO9E:
+- Wild Arms 2
+- Wild Arms II
+https://www.youtube.com/watch?v=njoqMF6xebE:
+- 'Chip ''n Dale: Rescue Rangers'
+https://www.youtube.com/watch?v=nl57xFzDIM0:
+- Darksiders II
+- Darksiders 2
+https://www.youtube.com/watch?v=novAJAlNKHk:
+- Wild Arms 4
+- Wild Arms IV
+https://www.youtube.com/watch?v=nrNPfvs4Amc:
+- Super Turrican 2
+- Super Turrican II
+https://www.youtube.com/watch?v=nuUYTK61228:
+- Hyper Light Drifter
+https://www.youtube.com/watch?v=nuXnaXgt2MM:
+- Super Mario 64
+https://www.youtube.com/watch?v=nvv6JrhcQSo:
+- 'The Legend of Zelda: Spirit Tracks'
+- spirit tracks
+https://www.youtube.com/watch?v=ny2ludAFStY:
+- Sonic Triple Trouble
+https://www.youtube.com/watch?v=o0t8vHJtaKk:
+- Rayman
+https://www.youtube.com/watch?v=o5tflPmrT5c:
+- I am Setsuna
+- 1 am Setsuna
+https://www.youtube.com/watch?v=o8cQl5pL6R8:
+- Street Fighter IV
+- Street Fighter 4
+https://www.youtube.com/watch?v=oEEm45iRylE:
+- Super Princess Peach
+https://www.youtube.com/watch?v=oFbVhFlqt3k:
+- Xenogears
+https://www.youtube.com/watch?v=oHjt7i5nt8w:
+- Final Fantasy VI
+- Final Fantasy 6
+https://www.youtube.com/watch?v=oIPYptk_eJE:
+- 'Starcraft II: Wings of Liberty'
+- 'Starcraft 2: Wings of Liberty'
+https://www.youtube.com/watch?v=oJFAAWYju6c:
+- Ys Origin
+https://www.youtube.com/watch?v=oNjnN_p5Clo:
+- Bomberman 64
+https://www.youtube.com/watch?v=oPjI-qh3QWQ:
+- Opoona
+https://www.youtube.com/watch?v=oQVscNAg1cQ:
+- The Adventures of Bayou Billy
+https://www.youtube.com/watch?v=oYOdCD4mWsk:
+- Donkey Kong Country 2
+- Donkey Kong Country II
+https://www.youtube.com/watch?v=o_vtaSXF0WU:
+- 'Arc the Lad IV: Twilight of the Spirits'
+- 'Arc the Lad 4: Twilight of the Spirits'
+https://www.youtube.com/watch?v=obPhMUJ8G9k:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=ocVRCl9Kcus:
+- Shatter
+https://www.youtube.com/watch?v=odyd0b_WZ9E:
+- Dr. Mario
+https://www.youtube.com/watch?v=oeBGiKhMy-Q:
+- Chrono Cross
+https://www.youtube.com/watch?v=oksAhZuJ55I:
+- Etrian Odyssey II
+- Etrian Odyssey 2
+https://www.youtube.com/watch?v=ol2zCdVl3pQ:
+- Final Fantasy Mystic Quest
+https://www.youtube.com/watch?v=ooohjN5k5QE:
+- Cthulhu Saves the World
+https://www.youtube.com/watch?v=oseD00muRc8:
+- 'Phoenix Wright: Trials and Tribulations'
+https://www.youtube.com/watch?v=ouV9XFnqgio:
+- Final Fantasy Tactics
+https://www.youtube.com/watch?v=oubq22rV9sE:
+- Top Gear Rally
+https://www.youtube.com/watch?v=p-GeFCcmGzk:
+- World of Warcraft
+https://www.youtube.com/watch?v=p-dor7Fj3GM:
+- 'The Legend of Zelda: Majora''s Mask'
+- 'majora''s mask'
+https://www.youtube.com/watch?v=p2vEakoOIdU:
+- Final Fantasy Mystic Quest
+https://www.youtube.com/watch?v=p48dpXQixgk:
+- Beyond Good & Evil
+- beyond good and evil
+https://www.youtube.com/watch?v=p5ObFGkl_-4:
+- 'The Legend of Zelda: A Link to the Past'
+- a link to the past
+https://www.youtube.com/watch?v=p6LMIrRG16c:
+- Asterix & Obelix
+- asterix and obelix
+https://www.youtube.com/watch?v=p6alE3r44-E:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=p9Nt449SP24:
+- Guardian's Crusade
+https://www.youtube.com/watch?v=pAlhuLOMFbU:
+- World of Goo
+https://www.youtube.com/watch?v=pDW_nN8EkjM:
+- Wild Guns
+https://www.youtube.com/watch?v=pDznNHFE5rA:
+- Final Fantasy Tactics Advance
+https://www.youtube.com/watch?v=pETxZAqgYgQ:
+- 'Zelda II: The Adventure of Link'
+- 'Zelda 2: The Adventure of Link'
+https://www.youtube.com/watch?v=pI4lS0lxV18:
+- Terraria
+https://www.youtube.com/watch?v=pIC5D1F9EQQ:
+- 'Zelda II: The Adventure of Link'
+- 'Zelda 2: The Adventure of Link'
+https://www.youtube.com/watch?v=pOK5gWEnEPY:
+- Resident Evil REmake
+https://www.youtube.com/watch?v=pQVuAGSKofs:
+- Super Castlevania IV
+- Super Castlevania 4
+https://www.youtube.com/watch?v=pTp4d38cPtc:
+- Super Metroid
+https://www.youtube.com/watch?v=pWVxGmFaNFs:
+- Ragnarok Online II
+- Ragnarok Online 2
+https://www.youtube.com/watch?v=pYSlMtpYKgw:
+- Final Fantasy XII
+- Final Fantasy 12
+https://www.youtube.com/watch?v=pZBBZ77gob4:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=pb3EJpfIYGc:
+- Persona 5
+- Persona V
+https://www.youtube.com/watch?v=pbmKt4bb5cs:
+- Brave Fencer Musashi
+https://www.youtube.com/watch?v=pfe5a22BHGk:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=pgacxbSdObw:
+- Breath of Fire IV
+- Breath of Fire 4
+https://www.youtube.com/watch?v=pieNm70nCIQ:
+- Baten Kaitos
+https://www.youtube.com/watch?v=pmKP4hR2Td0:
+- Mario Paint
+https://www.youtube.com/watch?v=pmQu1KRUw7U:
+- New Super Mario Bros Wii
+https://www.youtube.com/watch?v=pnS50Lmz6Y8:
+- 'Beyond: Two Souls'
+https://www.youtube.com/watch?v=pqCxONuUK3s:
+- 'Persona Q: Shadow of the Labyrinth'
+https://www.youtube.com/watch?v=pq_nXXuZTtA:
+- Phantasy Star Online
+https://www.youtube.com/watch?v=prRDZPbuDcI:
+- Guilty Gear XX Reload (Korean Version)
+- Guilty Gear 20 Reload
+- guilty gear xx reload
+https://www.youtube.com/watch?v=prc_7w9i_0Q:
+- Super Mario RPG
+https://www.youtube.com/watch?v=ptr9JCSxeug:
+- Popful Mail
+https://www.youtube.com/watch?v=pucNWokmRr0:
+- Tales of Eternia
+https://www.youtube.com/watch?v=pwIy1Oto4Qc:
+- Dark Cloud
+https://www.youtube.com/watch?v=pxAH2U75BoM:
+- Guild Wars 2
+- Guild Wars II
+https://www.youtube.com/watch?v=pxcx_BACNQE:
+- Double Dragon
+https://www.youtube.com/watch?v=pyO56W8cysw:
+- Machinarium
+https://www.youtube.com/watch?v=q-Fc23Ksh7I:
+- Final Fantasy V
+- Final Fantasy 5
+https://www.youtube.com/watch?v=q-NUnKMEXnM:
+- Evoland II
+- Evoland 2
+https://www.youtube.com/watch?v=q5vG69CXgRs:
+- Pokemon Ruby / Sapphire / Emerald
+- pokemon ruby
+- pokemon sapphire
+- pokemon emerald
+https://www.youtube.com/watch?v=q6btinyHMAg:
+- Okamiden
+https://www.youtube.com/watch?v=q87OASJOKOg:
+- 'Castlevania: Aria of Sorrow'
+https://www.youtube.com/watch?v=q8ZKmxmWqhY:
+- Massive Assault
+https://www.youtube.com/watch?v=qBC7aIoDSHU:
+- 'Minecraft: Story Mode'
+https://www.youtube.com/watch?v=qBh4tvmT6N4:
+- Max Payne 3
+- Max Payne III
+https://www.youtube.com/watch?v=qEozZuqRbgQ:
+- Snowboard Kids 2
+- Snowboard Kids II
+https://www.youtube.com/watch?v=qH8MFPIvFpU:
+- Xenogears
+https://www.youtube.com/watch?v=qJMfgv5YFog:
+- Katamari Damacy
+https://www.youtube.com/watch?v=qMkvXCaxXOg:
+- Senko no Ronde DUO
+https://www.youtube.com/watch?v=qN32pn9abhI:
+- Secret of Mana
+https://www.youtube.com/watch?v=qNIAYDOCfGU:
+- Guacamelee!
+https://www.youtube.com/watch?v=qP_40IXc-UA:
+- Mutant Mudds
+https://www.youtube.com/watch?v=qPgoOxGb6vk:
+- For the Frog the Bell Tolls
+https://www.youtube.com/watch?v=qR8x99ylgqc:
+- Axiom Verge
+https://www.youtube.com/watch?v=q_ClDJNpFV8:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=qcf1CdKVATo:
+- Jurassic Park
+https://www.youtube.com/watch?v=qgJ0yQNX2cI:
+- A Boy And His Blob
+https://www.youtube.com/watch?v=qhYbg4fsPiE:
+- Pokemon Quest
+https://www.youtube.com/watch?v=qjNHwF3R-kg:
+- Starbound
+https://www.youtube.com/watch?v=qmeaNH7mWAY:
+- 'Animal Crossing: New Leaf'
+https://www.youtube.com/watch?v=qmvx5zT88ww:
+- Sonic the Hedgehog
+https://www.youtube.com/watch?v=qnJDEN-JOzY:
+- Ragnarok Online II
+- Ragnarok Online 2
+https://www.youtube.com/watch?v=qnvYRm_8Oy8:
+- Shenmue
+https://www.youtube.com/watch?v=qqa_pXXSMDg:
+- Panzer Dragoon Saga
+https://www.youtube.com/watch?v=qrmzQOAASXo:
+- Kirby's Return to Dreamland
+https://www.youtube.com/watch?v=qs3lqJnkMX8:
+- Kirby's Epic Yarn
+https://www.youtube.com/watch?v=qsDU8LC5PLI:
+- 'Tactics Ogre: Let Us Cling Together'
+https://www.youtube.com/watch?v=qtrFSnyvEX0:
+- Demon's Crest
+https://www.youtube.com/watch?v=qyrLcNCBnPQ:
+- Sonic Unleashed
+https://www.youtube.com/watch?v=qzhWuriX9Ws:
+- Xenogears
+https://www.youtube.com/watch?v=r-zRrHICsw0:
+- LED Storm
+https://www.youtube.com/watch?v=r5A1MkzCX-s:
+- Castlevania
+https://www.youtube.com/watch?v=r5n9re80hcQ:
+- Dragon Quest VII 3DS
+- Dragon Quest 7 3DS
+https://www.youtube.com/watch?v=r6F92CUYjbI:
+- Titan Souls
+https://www.youtube.com/watch?v=r6dC9N4WgSY:
+- Lisa the Joyful
+https://www.youtube.com/watch?v=r7owYv6_tuw:
+- Tobal No. 1
+- Tobal No. I
+https://www.youtube.com/watch?v=rACVXsRX6IU:
+- Yoshi's Island DS
+https://www.youtube.com/watch?v=rADeZTd9qBc:
+- 'Ace Combat 5: The Unsung War'
+- 'Ace Combat V: The Unsung War'
+https://www.youtube.com/watch?v=rAJS58eviIk:
+- Ragnarok Online II
+- Ragnarok Online 2
+https://www.youtube.com/watch?v=rEE6yp873B4:
+- Contact
+https://www.youtube.com/watch?v=rFIzW_ET_6Q:
+- Shadow Hearts III
+- Shadow Hearts 3
+https://www.youtube.com/watch?v=rKGlXub23pw:
+- 'Ys VIII: Lacrimosa of Dana'
+- 'Ys 8: Lacrimosa of Dana'
+https://www.youtube.com/watch?v=rLM_wOEsOUk:
+- F-Zero
+https://www.youtube.com/watch?v=rLXgXfncaIA:
+- Anodyne
+https://www.youtube.com/watch?v=rLuP2pUwK8M:
+- Pop'n Music GB
+https://www.youtube.com/watch?v=rMxIyQqeGZ8:
+- Soma Bringer
+https://www.youtube.com/watch?v=rSBh2ZUKuq4:
+- 'Castlevania: Lament of Innocence'
+https://www.youtube.com/watch?v=rVmt7axswLo:
+- Castlevania Curse of Darkness
+https://www.youtube.com/watch?v=rXNrtuz0vl4:
+- Mega Man ZX
+https://www.youtube.com/watch?v=rXlxR7sH3iU:
+- Katamari Damacy
+https://www.youtube.com/watch?v=rY3n4qQZTWY:
+- Dragon Quest III
+- Dragon Quest 3
+https://www.youtube.com/watch?v=rZ2sNdqELMY:
+- Pikmin
+https://www.youtube.com/watch?v=rZn6QE_iVzA:
+- Tekken 5
+- Tekken V
+https://www.youtube.com/watch?v=rb9yuLqoryU:
+- Spelunky
+https://www.youtube.com/watch?v=rcnkZCiwKPs:
+- Trine
+https://www.youtube.com/watch?v=rd3QIW5ds4Q:
+- Spanky's Quest
+https://www.youtube.com/watch?v=reOJi31i9JM:
+- Chrono Trigger
+https://www.youtube.com/watch?v=rg_6OKlgjGE:
+- Shin Megami Tensei IV
+- Shin Megami Tensei 4
+https://www.youtube.com/watch?v=rhCzbGrG7DU:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=rhaQM_Vpiko:
+- Outlaws
+https://www.youtube.com/watch?v=rhbGqHurV5I:
+- Castlevania II
+- Castlevania 2
+https://www.youtube.com/watch?v=rltCi97DQ7Y:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=ro4ceM17QzY:
+- Sonic Lost World
+https://www.youtube.com/watch?v=roRsBf_kQps:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=rt0hrHroPz8:
+- 'Phoenix Wright: Ace Attorney'
+https://www.youtube.com/watch?v=ru4pkshvp7o:
+- Rayman Origins
+https://www.youtube.com/watch?v=rzLD0vbOoco:
+- 'Dracula X: Rondo of Blood'
+https://www.youtube.com/watch?v=rz_aiHo3aJg:
+- ActRaiser
+https://www.youtube.com/watch?v=s-6L1lM_x7k:
+- Final Fantasy XI
+- Final Fantasy 11
+https://www.youtube.com/watch?v=s-kTMBeDy40:
+- Grandia
+https://www.youtube.com/watch?v=s0mCsa2q2a4:
+- Donkey Kong Country 3
+- Donkey Kong Country III
+https://www.youtube.com/watch?v=s25IVZL0cuE:
+- 'Castlevania: Portrait of Ruin'
+https://www.youtube.com/watch?v=s3ja0vTezhs:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=s4pG2_UOel4:
+- Castlevania III
+- Castlevania 3
+https://www.youtube.com/watch?v=s6D8clnSE_I:
+- Pokemon
+https://www.youtube.com/watch?v=s7mVzuPSvSY:
+- Gravity Rush
+https://www.youtube.com/watch?v=sA_8Y30Lk2Q:
+- 'Ys VI: The Ark of Napishtim'
+- 'Ys 6: The Ark of Napishtim'
+https://www.youtube.com/watch?v=sBkqcoD53eI:
+- Breath of Fire II
+- Breath of Fire 2
+https://www.youtube.com/watch?v=sC4xMC4sISU:
+- Bioshock
+https://www.youtube.com/watch?v=sHQslJ2FaaM:
+- Krater
+https://www.youtube.com/watch?v=sHduBNdadTA:
+- 'Atelier Iris 2: The Azoth of Destiny'
+- 'Atelier Iris II: The Azoth of Destiny'
+https://www.youtube.com/watch?v=sIXnwB5AyvM:
+- Alundra
+https://www.youtube.com/watch?v=sMcx87rq0oA:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=sN8gtvYdqY4:
+- Opoona
+https://www.youtube.com/watch?v=sOgo6fXbJI4:
+- Final Fantasy Mystic Quest
+https://www.youtube.com/watch?v=sQ4aADxHssY:
+- Romance of the Three Kingdoms V
+- Romance of the Three Kingdoms 5
+https://www.youtube.com/watch?v=sRLoAqxsScI:
+- 'Tintin: Prisoners of the Sun'
+https://www.youtube.com/watch?v=sSkcY8zPWIY:
+- 'The Legend of Zelda: Skyward Sword'
+- skyward sword
+https://www.youtube.com/watch?v=sUc3p5sojmw:
+- 'Zelda II: The Adventure of Link'
+- 'Zelda 2: The Adventure of Link'
+https://www.youtube.com/watch?v=sVnly-OASsI:
+- Lost Odyssey
+https://www.youtube.com/watch?v=sYVOk6kU3TY:
+- 'South Park: The Stick of Truth'
+https://www.youtube.com/watch?v=sZU8xWDH68w:
+- The World Ends With You
+https://www.youtube.com/watch?v=s_Z71tcVVVg:
+- Super Mario Sunshine
+https://www.youtube.com/watch?v=seJszC75yCg:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=seaPEjQkn74:
+- Emil Chronicle Online
+https://www.youtube.com/watch?v=shx_nhWmZ-I:
+- 'Adventure Time: Hey Ice King! Why''d You Steal Our Garbage?!'
+https://www.youtube.com/watch?v=sl22D3F1954:
+- Marvel vs Capcom 3
+- Marvel vs Capcom III
+https://www.youtube.com/watch?v=snsS40I9-Ts:
+- Shovel Knight
+https://www.youtube.com/watch?v=sqIb-ZhY85Q:
+- Mega Man 5
+- Mega Man V
+https://www.youtube.com/watch?v=su8bqSqIGs0:
+- Chrono Trigger
+https://www.youtube.com/watch?v=sx6L5b-ACVk:
+- 'The Legend of Zelda: A Link to the Past'
+- a link to the past
+https://www.youtube.com/watch?v=sxcTW6DlNqA:
+- Super Adventure Island
+https://www.youtube.com/watch?v=szxxAefjpXw:
+- 'Mario & Luigi: Bowser''s Inside Story'
+- 'mario and luigi: bower''s inside story'
+https://www.youtube.com/watch?v=t6-MOj2mkgE:
+- '999: Nine Hours, Nine Persons, Nine Doors'
+https://www.youtube.com/watch?v=t6YVE2kp8gs:
+- Earthbound
+https://www.youtube.com/watch?v=t97-JQtcpRA:
+- Nostalgia
+https://www.youtube.com/watch?v=t9uUD60LS38:
+- 'SMT: Digital Devil Saga 2'
+- 'SMT: Digital Devil Saga II'
+https://www.youtube.com/watch?v=tBr9OyNHRjA:
+- 'Sang-Froid: Tales of Werewolves'
+https://www.youtube.com/watch?v=tDuCLC_sZZY:
+- 'Divinity: Original Sin'
+https://www.youtube.com/watch?v=tEXf3XFGFrY:
+- Sonic Unleashed
+https://www.youtube.com/watch?v=tFsVKUoGJZs:
+- Deus Ex
+https://www.youtube.com/watch?v=tIiNJgLECK0:
+- Super Mario RPG
+https://www.youtube.com/watch?v=tKMWMS7O50g:
+- Pokemon Mystery Dungeon
+https://www.youtube.com/watch?v=tKmmcOH2xao:
+- VVVVVV
+https://www.youtube.com/watch?v=tL3zvui1chQ:
+- 'The Legend of Zelda: Majora''s Mask'
+- 'majora''s mask'
+https://www.youtube.com/watch?v=tNvY96zReis:
+- Metal Gear Solid
+https://www.youtube.com/watch?v=tQYCO5rHSQ8:
+- Donkey Kong Land
+https://www.youtube.com/watch?v=tU3ZA2tFxDU:
+- Fatal Frame
+https://www.youtube.com/watch?v=tVnjViENsds:
+- Super Robot Wars 4
+- Super Robot Wars IV
+https://www.youtube.com/watch?v=tWopcEQUkhg:
+- Super Smash Bros. Wii U
+https://www.youtube.com/watch?v=tXnCJLLZIvc:
+- 'Paper Mario: The Thousand Year Door'
+https://www.youtube.com/watch?v=tbVLmRfeIQg:
+- Alundra
+https://www.youtube.com/watch?v=tdsnX2Z0a3g:
+- Blast Corps
+https://www.youtube.com/watch?v=tgxFLMM9TLw:
+- Lost Odyssey
+https://www.youtube.com/watch?v=tiL0mhmOOnU:
+- Sleepwalker
+https://www.youtube.com/watch?v=tiwsAs6WVyQ:
+- Castlevania II
+- Castlevania 2
+https://www.youtube.com/watch?v=tj3ks8GfBQU:
+- Guilty Gear XX Reload (Korean Version)
+- Guilty Gear 20 Reload
+- guilty gear xx reload
+https://www.youtube.com/watch?v=tk61GaJLsOQ:
+- Mother 3
+- Mother III
+https://www.youtube.com/watch?v=tkj57nM0OqQ:
+- Sonic the Hedgehog 2
+- Sonic the Hedgehog II
+https://www.youtube.com/watch?v=tlY88rlNnEE:
+- Xenogears
+https://www.youtube.com/watch?v=tnAXbjXucPc:
+- Battletoads & Double Dragon
+- battletoads
+- double dragon
+https://www.youtube.com/watch?v=tqyigq3uWzo:
+- Perfect Dark
+https://www.youtube.com/watch?v=tvGn7jf7t8c:
+- Shinobi
+https://www.youtube.com/watch?v=tvjGxtbJpMk:
+- Parasite Eve
+https://www.youtube.com/watch?v=ty4CBnWeEKE:
+- Mario Kart 64
+https://www.youtube.com/watch?v=tzi9trLh9PE:
+- Blue Dragon
+https://www.youtube.com/watch?v=u3S8CGo_klk:
+- Radical Dreamers
+https://www.youtube.com/watch?v=u4cv8dOFQsc:
+- Silent Hill 3
+- Silent Hill III
+https://www.youtube.com/watch?v=u5v8qTkf-yk:
+- Super Smash Bros. Brawl
+https://www.youtube.com/watch?v=u6Fa28hef7I:
+- Last Bible III
+- Last Bible 3
+https://www.youtube.com/watch?v=uDwLy1_6nDw:
+- F-Zero
+https://www.youtube.com/watch?v=uDzUf4I751w:
+- Mega Man Battle Network
+https://www.youtube.com/watch?v=uKK631j464M:
+- 'Castlevania: Symphony of the Night'
+https://www.youtube.com/watch?v=uKWkvGnNffU:
+- Dustforce
+https://www.youtube.com/watch?v=uM3dR2VbMck:
+- Super Stickman Golf 2
+- Super Stickman Golf II
+https://www.youtube.com/watch?v=uPU4gCjrDqg:
+- StarTropics
+https://www.youtube.com/watch?v=uSOspFMHVls:
+- Super Mario Bros 2
+- Super Mario Bros II
+https://www.youtube.com/watch?v=uTRjJj4UeCg:
+- Shovel Knight
+https://www.youtube.com/watch?v=uUA40z9kT6w:
+- Tomb Raider Legend
+https://www.youtube.com/watch?v=uURUC6yEMZc:
+- Yooka-Laylee
+https://www.youtube.com/watch?v=uV_g76ThygI:
+- Xenosaga III
+- Xenosaga 3
+https://www.youtube.com/watch?v=uX-Dk8gBgg8:
+- Valdis Story
+https://www.youtube.com/watch?v=uYX350EdM-8:
+- Secret of Mana
+https://www.youtube.com/watch?v=uYcqP1TWYOE:
+- Soul Blade
+https://www.youtube.com/watch?v=ucXYUeyQ6iM:
+- Pop'n Music 2
+- Pop'n Music II
+https://www.youtube.com/watch?v=udEC_I8my9Y:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=udNOf4W52hg:
+- Dragon Quest III
+- Dragon Quest 3
+https://www.youtube.com/watch?v=udO3kaNWEsI:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=uixqfTElRuI:
+- 'The Legend of Zelda: Wind Waker'
+- 'wind waker'
+https://www.youtube.com/watch?v=uj2mhutaEGA:
+- Ghouls 'n' Ghosts
+https://www.youtube.com/watch?v=ujverEHBzt8:
+- F-Zero GX
+https://www.youtube.com/watch?v=umh0xNPh-pY:
+- Okami
+https://www.youtube.com/watch?v=un-CZxdgudA:
+- Vortex
+https://www.youtube.com/watch?v=ung2q_BVXWY:
+- Nora to Toki no Koubou
+https://www.youtube.com/watch?v=uvRU3gsmXx4:
+- 'Qbeh-1: The Atlas Cube'
+- 'Qbeh-I: The Atlas Cube'
+https://www.youtube.com/watch?v=uwB0T1rExMc:
+- Drakkhen
+https://www.youtube.com/watch?v=uxETfaBcSYo:
+- Grandia II
+- Grandia 2
+https://www.youtube.com/watch?v=uy2OQ0waaPo:
+- Secret of Evermore
+https://www.youtube.com/watch?v=v-h3QCB_Pig:
+- Ninja Gaiden II
+- Ninja Gaiden 2
+https://www.youtube.com/watch?v=v02ZFogqSS8:
+- Pokemon Diamond / Pearl / Platinum
+- pokemon diamond
+- pokemon pearl
+- pokemon platinum
+https://www.youtube.com/watch?v=v0toUGs93No:
+- 'Command & Conquer: Tiberian Sun'
+- 'command and conquer: tiberian sun'
+https://www.youtube.com/watch?v=v4fgFmfuzqc:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=vCqkxI9eu44:
+- Astal
+https://www.youtube.com/watch?v=vEx9gtmDoRI:
+- 'Star Ocean 2: The Second Story'
+- 'Star Ocean II: The Second Story'
+https://www.youtube.com/watch?v=vLRhuxHiYio:
+- Hotline Miami 2
+- Hotline Miami II
+https://www.youtube.com/watch?v=vLkDLzEcJlU:
+- 'Final Fantasy VII: CC'
+- 'Final Fantasy 7: 200'
+https://www.youtube.com/watch?v=vMNf5-Y25pQ:
+- Final Fantasy V
+- Final Fantasy 5
+https://www.youtube.com/watch?v=vN9zJNpH3Mc:
+- Terranigma
+https://www.youtube.com/watch?v=vRRrOKsfxPg:
+- 'The Legend of Zelda: A Link to the Past'
+- a link to the past
+https://www.youtube.com/watch?v=vY8in7gADDY:
+- Tetrisphere
+https://www.youtube.com/watch?v=vZOCpswBNiw:
+- Lufia
+https://www.youtube.com/watch?v=v_9EdBLmHcE:
+- Equinox
+https://www.youtube.com/watch?v=vaJvNNWO_OQ:
+- Mega Man 2
+- Mega Man II
+https://www.youtube.com/watch?v=vbzmtIEujzk:
+- Deadly Premonition
+https://www.youtube.com/watch?v=vfRr0Y0QnHo:
+- Super Mario 64
+https://www.youtube.com/watch?v=vfqMK4BuN64:
+- Beyond Good & Evil
+- beyond good and evil
+https://www.youtube.com/watch?v=vgD6IngCtyU:
+- 'Romancing Saga: Minstrel Song'
+https://www.youtube.com/watch?v=vl6Cuvw4iyk:
+- Mighty Switch Force!
+https://www.youtube.com/watch?v=vmUwR3aa6dc:
+- F-Zero
+https://www.youtube.com/watch?v=vp6NjZ0cGiI:
+- Deep Labyrinth
+https://www.youtube.com/watch?v=vrWC1PosXSI:
+- Legend of Dragoon
+https://www.youtube.com/watch?v=vsLJDafIEHc:
+- Legend of Grimrock
+https://www.youtube.com/watch?v=vsYHDEiBSrg:
+- 'The Legend of Zelda: Twilight Princess'
+- twilight princess
+https://www.youtube.com/watch?v=vtTk81cIJlg:
+- Magnetis
+https://www.youtube.com/watch?v=vz59icOE03E:
+- Boot Hill Heroes
+https://www.youtube.com/watch?v=w1tmFpEPagk:
+- Suikoden
+https://www.youtube.com/watch?v=w2HT5XkGaws:
+- 'Gremlins 2: The New Batch'
+- 'Gremlins II: The New Batch'
+https://www.youtube.com/watch?v=w4J4ZQP7Nq0:
+- Legend of Mana
+https://www.youtube.com/watch?v=w4b-3x2wqpw:
+- Breath of Death VII
+- Breath of Death 7
+https://www.youtube.com/watch?v=w6exvhdhIE8:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=w7dO2edfy00:
+- Wild Arms
+https://www.youtube.com/watch?v=wBAXLY1hq7s:
+- Ys Chronicles
+https://www.youtube.com/watch?v=wBUVdh4mVDc:
+- Lufia
+https://www.youtube.com/watch?v=wE8p0WBW4zo:
+- Guilty Gear XX Reload (Korean Version)
+- Guilty Gear 20 Reload
+- guilty gear xx reload
+https://www.youtube.com/watch?v=wEkfscyZEfE:
+- Sonic Rush
+https://www.youtube.com/watch?v=wFJYhWhioPI:
+- Shin Megami Tensei Nocturne
+https://www.youtube.com/watch?v=wFM8K7GLFKk:
+- Goldeneye
+https://www.youtube.com/watch?v=wHgmFPLNdW8:
+- Pop'n Music 8
+- Pop'n Music VIII
+https://www.youtube.com/watch?v=wKNz1SsO_cM:
+- Life is Strange
+https://www.youtube.com/watch?v=wKgoegkociY:
+- Opoona
+https://www.youtube.com/watch?v=wNfUOcOv1no:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=wPCmweLoa8Q:
+- Wangan Midnight Maximum Tune 3
+- Wangan Midnight Maximum Tune III
+https://www.youtube.com/watch?v=wPZgCJwBSgI:
+- Perfect Dark
+https://www.youtube.com/watch?v=wXSFR4tDIUs:
+- F-Zero
+https://www.youtube.com/watch?v=wXXgqWHDp18:
+- Tales of Zestiria
+https://www.youtube.com/watch?v=wXZ-2p4rC5s:
+- 'Metroid II: Return of Samus'
+- 'Metroid 2: Return of Samus'
+https://www.youtube.com/watch?v=w_6ZSQ2_7Q4:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=w_N7__8-9r0:
+- Donkey Kong Land
+https://www.youtube.com/watch?v=w_TOt-XQnPg:
+- Pokemon Ruby / Sapphire / Emerald
+- pokemon ruby
+- pokemon sapphire
+- pokemon emerald
+https://www.youtube.com/watch?v=waesdKG4rhM:
+- Dragon Quest VII 3DS
+- Dragon Quest 7 3DS
+https://www.youtube.com/watch?v=wdWZYggy75A:
+- Mother 4
+- Mother IV
+https://www.youtube.com/watch?v=wgAtWoPfBgQ:
+- Metroid Prime
+https://www.youtube.com/watch?v=wkrgYK2U5hE:
+- 'Super Monkey Ball: Step & Roll'
+- 'super monkey ball: step and roll'
+https://www.youtube.com/watch?v=wqb9Cesq3oM:
+- Super Mario RPG
+https://www.youtube.com/watch?v=wuFKdtvNOp0:
+- Granado Espada
+https://www.youtube.com/watch?v=wv6HHTa4jjI:
+- Miitopia
+https://www.youtube.com/watch?v=ww6KySR4MQ0:
+- Street Fighter II
+- Street Fighter 2
+https://www.youtube.com/watch?v=wxzrrUWOU8M:
+- Space Station Silicon Valley
+https://www.youtube.com/watch?v=wyYpZvfAUso:
+- Soul Calibur
+https://www.youtube.com/watch?v=x0wxJHbcDYE:
+- Prop Cycle
+https://www.youtube.com/watch?v=x4i7xG2IOOE:
+- 'Silent Hill: Origins'
+https://www.youtube.com/watch?v=x4mrK-42Z18:
+- Sonic Generations
+https://www.youtube.com/watch?v=x6VlzkDSU6k:
+- Parasite Eve
+https://www.youtube.com/watch?v=x9S3GnJ3_WQ:
+- Wild Arms
+https://www.youtube.com/watch?v=xFUvAJTiSH4:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=xJHVfLI5pLY:
+- 'Animal Crossing: Wild World'
+https://www.youtube.com/watch?v=xKxhEqH7UU0:
+- ICO
+https://www.youtube.com/watch?v=xP3PDznPrw4:
+- Dragon Quest IX
+- Dragon Quest 9
+https://www.youtube.com/watch?v=xTRmnisEJ7Y:
+- Super Mario Galaxy
+https://www.youtube.com/watch?v=xTxZchmHmBw:
+- Asterix
+https://www.youtube.com/watch?v=xWQOYiYHZ2w:
+- Antichamber
+https://www.youtube.com/watch?v=xWVBra_NpZo:
+- Bravely Default
+https://www.youtube.com/watch?v=xY86oDk6Ces:
+- Super Street Fighter II
+- Super Street Fighter 2
+https://www.youtube.com/watch?v=xZHoULMU6fE:
+- Firewatch
+https://www.youtube.com/watch?v=xaKXWFIz5E0:
+- Dragon Ball Z Butouden 2
+- Dragon Ball Z Butouden II
+https://www.youtube.com/watch?v=xajMfQuVnp4:
+- 'OFF'
+https://www.youtube.com/watch?v=xdQDETzViic:
+- The 7th Saga
+https://www.youtube.com/watch?v=xfzWn5b6MHM:
+- 'Silent Hill: Origins'
+https://www.youtube.com/watch?v=xgn1eHG_lr8:
+- Total Distortion
+https://www.youtube.com/watch?v=xhVwxYU23RU:
+- Bejeweled 3
+- Bejeweled III
+https://www.youtube.com/watch?v=xhgVOEt-wOo:
+- Final Fantasy VIII
+- Final Fantasy 8
+https://www.youtube.com/watch?v=xhzySCD19Ss:
+- 'The Legend of Zelda: Twilight Princess'
+- twilight princess
+https://www.youtube.com/watch?v=xieau-Uia18:
+- Sonic the Hedgehog (2006)
+- Sonic the Hedgehog 2006
+- sonic the hedgehog 06
+https://www.youtube.com/watch?v=xj0AV3Y-gFU:
+- 'The Legend of Zelda: Wind Waker'
+- wind waker
+https://www.youtube.com/watch?v=xkSD3pCyfP4:
+- Gauntlet
+https://www.youtube.com/watch?v=xl30LV6ruvA:
+- Fantasy Life
+https://www.youtube.com/watch?v=xorfsUKMGm8:
+- 'Shadow Hearts II: Covenant'
+- 'Shadow Hearts 2: Covenant'
+https://www.youtube.com/watch?v=xpu0N_oRDrM:
+- Final Fantasy X
+- Final Fantasy 10
+https://www.youtube.com/watch?v=xrLiaewZZ2E:
+- 'The Legend of Zelda: Twilight Princess'
+- twilight princess
+https://www.youtube.com/watch?v=xsC6UGAJmIw:
+- Waterworld
+https://www.youtube.com/watch?v=xtsyXDTAWoo:
+- Tetris Attack
+https://www.youtube.com/watch?v=xuCzPu3tHzg:
+- Seiken Densetsu 3
+- Seiken Densetsu III
+https://www.youtube.com/watch?v=xvvXFCYVmkw:
+- Mario Kart 64
+https://www.youtube.com/watch?v=xx9uLg6pYc0:
+- 'Spider-Man & X-Men: Arcade''s Revenge'
+- 'Spider-Man and X-Men: Arcade''s Revenge'
+https://www.youtube.com/watch?v=xze4yNQAmUU:
+- Mega Man 10
+https://www.youtube.com/watch?v=xzfhOQampfs:
+- Suikoden II
+- Suikoden 2
+https://www.youtube.com/watch?v=xzmv8C2I5ek:
+- 'Lightning Returns: Final Fantasy XIII'
+- 'Lightning Returns: Final Fantasy 13'
+https://www.youtube.com/watch?v=y0PixBaf8_Y:
+- Waterworld
+https://www.youtube.com/watch?v=y2MP97fwOa8:
+- Pokemon Ruby / Sapphire / Emerald
+- pokemon ruby
+- pokemon sapphire
+- pokemon emerald
+https://www.youtube.com/watch?v=y4DAIZM2sTc:
+- Breath of Fire III
+- Breath of Fire 3
+https://www.youtube.com/watch?v=y6UhV3E2H6w:
+- Xenoblade Chronicles
+https://www.youtube.com/watch?v=y81PyRX4ENA:
+- Zone of the Enders 2
+- Zone of the Enders II
+https://www.youtube.com/watch?v=y9SFrBt1xtw:
+- 'Mario Kart: Double Dash!!'
+https://www.youtube.com/watch?v=yCLW8IXbFYM:
+- Suikoden V
+- Suikoden 5
+https://www.youtube.com/watch?v=yDMN8XKs1z0:
+- Sonic 3D Blast (Saturn)
+- sonic 3d blast
+https://www.youtube.com/watch?v=yERMMu-OgEo:
+- Final Fantasy IV
+- Final Fantasy 4
+https://www.youtube.com/watch?v=yF_f-Y-MD2o:
+- Final Fantasy IX
+- Final Fantasy 9
+https://www.youtube.com/watch?v=yJrRo8Dqpkw:
+- 'Mario Kart: Double Dash !!'
+- 'mario kart double dash'
+https://www.youtube.com/watch?v=yTe_L2AYaI4:
+- Legend of Dragoon
+https://www.youtube.com/watch?v=yV7eGX8y2dM:
+- Hotline Miami 2
+- Hotline Miami II
+https://www.youtube.com/watch?v=yVcn0cFJY_c:
+- Xenosaga II
+- Xenosaga 2
+https://www.youtube.com/watch?v=yYPNScB1alA:
+- Speed Freaks
+https://www.youtube.com/watch?v=yZ5gFAjZsS4:
+- Wolverine
+https://www.youtube.com/watch?v=y_4Ei9OljBA:
+- 'The Legend of Zelda: Minish Cap'
+- minish cap
+https://www.youtube.com/watch?v=ye960O2B3Ao:
+- Star Fox
+https://www.youtube.com/watch?v=ygqp3eNXbI4:
+- Pop'n Music 18
+- Pop'n Music XVIII
+https://www.youtube.com/watch?v=yh8dWsIVCD8:
+- Battletoads
+https://www.youtube.com/watch?v=yirRajMEud4:
+- Jet Set Radio
+https://www.youtube.com/watch?v=yr7fU3D0Qw4:
+- Final Fantasy VII
+- Final Fantasy 7
+https://www.youtube.com/watch?v=ysLhWVbE12Y:
+- Panzer Dragoon
+https://www.youtube.com/watch?v=ysoz5EnW6r4:
+- Tekken 7
+- Tekken VII
+https://www.youtube.com/watch?v=ytp_EVRf8_I:
+- Super Mario RPG
+https://www.youtube.com/watch?v=yz1yrVcpWjA:
+- Persona 3
+- Persona III
+https://www.youtube.com/watch?v=yzgSscW7klw:
+- Steamworld Dig 2
+- Steamworld Dig II
+https://www.youtube.com/watch?v=z-QISdXXN_E:
+- Wild Arms Alter Code F
+https://www.youtube.com/watch?v=z1oW4USdB68:
+- 'The Legend of Zelda: Minish Cap'
+- minish cap
+https://www.youtube.com/watch?v=z513Tty2hag:
+- Fez
+https://www.youtube.com/watch?v=z5ndH9xEVlo:
+- Streets of Rage
+https://www.youtube.com/watch?v=z5oERC4cD8g:
+- Skies of Arcadia
+https://www.youtube.com/watch?v=zB-n8fx-Dig:
+- Kirby's Return to Dreamland
+https://www.youtube.com/watch?v=zCP7hfY8LPo:
+- Ape Escape
+https://www.youtube.com/watch?v=zFfgwmWuimk:
+- 'DmC: Devil May Cry'
+- devil may cry
+https://www.youtube.com/watch?v=zHej43S_OMI:
+- Radiata Stories
+https://www.youtube.com/watch?v=zLXJ6l4Gxsg:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=zO9y6EH6jVw:
+- 'Metroid Prime 2: Echoes'
+- 'Metroid Prime II: Echoes'
+https://www.youtube.com/watch?v=zS8QlZkN_kM:
+- Guardian's Crusade
+https://www.youtube.com/watch?v=zTHAKsaD_8U:
+- Super Mario Bros
+https://www.youtube.com/watch?v=zTKEnlYhL0M:
+- 'Gremlins 2: The New Batch'
+- 'Gremlins II: The New Batch'
+https://www.youtube.com/watch?v=zTOZesa-uG4:
+- 'Turok: Dinosaur Hunter'
+https://www.youtube.com/watch?v=zYMU-v7GGW4:
+- Kingdom Hearts
+https://www.youtube.com/watch?v=za05W9gtegc:
+- Metroid Prime
+https://www.youtube.com/watch?v=zawNmXL36zM:
+- SpaceChem
+https://www.youtube.com/watch?v=ziyH7x0P5tU:
+- Final Fantasy
+https://www.youtube.com/watch?v=zojcLdL7UTw:
+- Shining Hearts
+https://www.youtube.com/watch?v=zpVIM8de2xw:
+- Mega Man 3
+- Mega Man III
+https://www.youtube.com/watch?v=zpleUx1Llgs:
+- Super Smash Bros Wii U / 3DS
+- super smash bros wii u
+- super smas bros 3ds
+https://www.youtube.com/watch?v=zqFtW92WUaI:
+- Super Mario World
+https://www.youtube.com/watch?v=zqgfOTBPv3E:
+- Metroid Prime 3
+- Metroid Prime III
+https://www.youtube.com/watch?v=zsuBQNO7tps:
+- Dark Souls
+https://www.youtube.com/watch?v=ztLD9IqnRqY:
+- Metroid Prime 3
+- Metroid Prime III
+https://www.youtube.com/watch?v=ztiSivmoE0c:
+- Breath of Fire
+https://www.youtube.com/watch?v=zxZROhq4Lz0:
+- Pop'n Music 8
+- Pop'n Music VIII
diff --git a/audiotrivia/data/lists/games.yaml b/audiotrivia/data/lists/games.yaml
new file mode 100644
index 0000000..4a823d8
--- /dev/null
+++ b/audiotrivia/data/lists/games.yaml
@@ -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
\ No newline at end of file
diff --git a/audiotrivia/data/lists/guitar.yaml b/audiotrivia/data/lists/guitar.yaml
new file mode 100644
index 0000000..1c0d07e
--- /dev/null
+++ b/audiotrivia/data/lists/guitar.yaml
@@ -0,0 +1,4 @@
+https://www.youtube.com/watch?v=hfyE220BsD0:
+- holiday
+https://www.youtube.com/watch?v=Hh3U9iPKeXQ:
+- sultans of swing
\ No newline at end of file
diff --git a/audiotrivia/data/lists/league.yaml b/audiotrivia/data/lists/league.yaml
new file mode 100644
index 0000000..323aadd
--- /dev/null
+++ b/audiotrivia/data/lists/league.yaml
@@ -0,0 +1,4 @@
+https://www.youtube.com/watch?v=Hi1kUdreiWk:
+- Jinx
+https://www.youtube.com/watch?v=PNYHFluhOGI:
+- Teemo
\ No newline at end of file
diff --git a/audiotrivia/info.json b/audiotrivia/info.json
new file mode 100644
index 0000000..519973e
--- /dev/null
+++ b/audiotrivia/info.json
@@ -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"
+ ]
+}
\ No newline at end of file
diff --git a/ccrole/ccrole.py b/ccrole/ccrole.py
index 38cff39..fee9a2a 100644
--- a/ccrole/ccrole.py
+++ b/ccrole/ccrole.py
@@ -1,16 +1,16 @@
-import discord
-import asyncio
-
-from discord.ext import commands
+import asyncio
+import re
+from typing import Any
+import discord
from redbot.core import Config, checks
-
+from redbot.core import commands
from redbot.core.utils.chat_formatting import pagify, box
-import os
-import re
+Cog: Any = getattr(commands, "Cog", object)
-class CCRole:
+
+class CCRole(Cog):
"""
Custom commands
Creates commands used to display text and adjust roles
@@ -20,23 +20,28 @@ class CCRole:
self.bot = bot
self.config = Config.get_conf(self, identifier=9999114111108101)
default_guild = {
- "cmdlist" : {},
+ "cmdlist": {},
"settings": {}
}
-
- self.config.register_guild(**default_guild)
+ self.config.register_guild(**default_guild)
- @commands.group(no_pm=True)
+ @commands.guild_only()
+ @commands.group()
async def ccrole(self, ctx):
- """Custom commands management"""
+ """Custom commands management with roles
+
+ Highly customizable custom commands with role management."""
if not ctx.invoked_subcommand:
- await ctx.send_help()
+ pass
@ccrole.command(name="add")
@checks.mod_or_permissions(administrator=True)
- async def ccrole_add(self, ctx, command : str):
- """Adds a custom command with roles"""
+ async def ccrole_add(self, ctx, command: str):
+ """Adds a custom command with roles
+
+ When adding text, put arguments in `{}` to eval them
+ Options: `{author}`, `{target}`, `{server}`, `{channel}`, `{message}`"""
command = command.lower()
if command in self.bot.all_commands:
await ctx.send("That command is already a standard command.")
@@ -45,125 +50,164 @@ class CCRole:
guild = ctx.guild
author = ctx.author
channel = ctx.channel
-
- cmdlist = self.config.guild(ctx.guild).cmdlist
-
- if await cmdlist.get_raw(command, default=None):
+
+ cmd_list = self.config.guild(guild).cmdlist
+
+ if await cmd_list.get_raw(command, default=None):
await ctx.send("This command already exists. Delete it with `{}ccrole delete` first.".format(ctx.prefix))
return
# Roles to add
await ctx.send('What roles should it add? (Must be **comma separated**)\nSay `None` to skip adding roles')
-
+
def check(m):
- return m.author == author and m.channel==channel
-
+ return m.author == author and m.channel == channel
+
try:
answer = await self.bot.wait_for('message', timeout=120, check=check)
except asyncio.TimeoutError:
await ctx.send("Timed out, canceling")
-
+ return
+
arole_list = []
- if answer.content.upper()!="NONE":
+ if answer.content.upper() != "NONE":
arole_list = await self._get_roles_from_content(ctx, answer.content)
if arole_list is None:
await ctx.send("Invalid answer, canceling")
return
-
+
# Roles to remove
await ctx.send('What roles should it remove? (Must be comma separated)\nSay `None` to skip removing roles')
try:
answer = await self.bot.wait_for('message', timeout=120, check=check)
except asyncio.TimeoutError:
await ctx.send("Timed out, canceling")
-
+ return
+
rrole_list = []
- if answer.content.upper()!="NONE":
+ if answer.content.upper() != "NONE":
rrole_list = await self._get_roles_from_content(ctx, answer.content)
if rrole_list is None:
await ctx.send("Invalid answer, canceling")
return
-
+
# Roles to use
- await ctx.send('What roles are allowed to use this command? (Must be comma separated)\nSay `None` to allow all roles')
-
+ await ctx.send(
+ 'What roles are allowed to use this command? (Must be comma separated)\nSay `None` to allow all roles')
+
try:
answer = await self.bot.wait_for('message', timeout=120, check=check)
except asyncio.TimeoutError:
await ctx.send("Timed out, canceling")
-
+ return
+
prole_list = []
- if answer.content.upper()!="NONE":
+ if answer.content.upper() != "NONE":
prole_list = await self._get_roles_from_content(ctx, answer.content)
if prole_list is None:
await ctx.send("Invalid answer, canceling")
return
-
+
# Selfrole
- await ctx.send('Is this a targeted command?(yes/no)\nNo will make this a selfrole command')
-
+ await ctx.send('Is this a targeted command?(yes//no)\nNo will make this a selfrole command')
+
try:
answer = await self.bot.wait_for('message', timeout=120, check=check)
except asyncio.TimeoutError:
await ctx.send("Timed out, canceling")
-
+ return
+
if answer.content.upper() in ["Y", "YES"]:
targeted = True
await ctx.send("This command will be **`targeted`**")
else:
targeted = False
await ctx.send("This command will be **`selfrole`**")
-
+
# Message to send
- await ctx.send('What message should the bot say when using this command?\nSay `None` to send the default `Success!` message')
-
+ await ctx.send(
+ 'What message should the bot say when using this command?\n'
+ 'Say `None` to send the default `Success!` message\n'
+ 'Eval Options: `{author}`, `{target}`, `{server}`, `{channel}`, `{message}`\n'
+ 'For example: `Welcome {target.mention} to {server.name}!`')
+
try:
answer = await self.bot.wait_for('message', timeout=120, check=check)
except asyncio.TimeoutError:
await ctx.send("Timed out, canceling")
+ return
+
text = "Success!"
- if answer.content.upper()!="NONE":
+ if answer.content.upper() != "NONE":
text = answer.content
# Save the command
-
+
out = {'text': text, 'aroles': arole_list, 'rroles': rrole_list, "proles": prole_list, "targeted": targeted}
-
- await cmdlist.set_raw(command, value=out)
-
- ctx.send("Custom Command **`{}`** successfully added".format(command))
-
+
+ await cmd_list.set_raw(command, value=out)
+
+ await ctx.send("Custom Command **`{}`** successfully added".format(command))
+
@ccrole.command(name="delete")
@checks.mod_or_permissions(administrator=True)
- async def ccrole_delete(self, ctx, command : str):
+ async def ccrole_delete(self, ctx, command: str):
"""Deletes a custom command
+
Example:
- [p]ccrole delete yourcommand"""
+ `[p]ccrole delete yourcommand`"""
guild = ctx.guild
command = command.lower()
- if not await self.config.guild(ctx.guild).cmdlist.get_raw(command, default=None):
+ if not await self.config.guild(guild).cmdlist.get_raw(command, default=None):
await ctx.send("That command doesn't exist")
else:
- await self.config.guild(ctx.guild).cmdlist.set_raw(command, value=None)
+ await self.config.guild(guild).cmdlist.set_raw(command, value=None)
await ctx.send("Custom command successfully deleted.")
+ @ccrole.command(name="details")
+ async def ccrole_details(self, ctx, command: str):
+ """Provide details about passed custom command"""
+ guild = ctx.guild
+ command = command.lower()
+ cmd = await self.config.guild(guild).cmdlist.get_raw(command, default=None)
+ if cmd is None:
+ await ctx.send("That command doesn't exist")
+ return
+
+ embed = discord.Embed(title=command,
+ description="{} custom command".format("Targeted" if cmd['targeted'] else "Non-Targeted"))
+
+ def process_roles(role_list):
+ if not role_list:
+ return "None"
+ return ", ".join([discord.utils.get(ctx.guild.roles, id=roleid).name for roleid in role_list])
+
+ embed.add_field(name="Text", value="```{}```".format(cmd['text']))
+ embed.add_field(name="Adds Roles", value=process_roles(cmd['aroles']), inline=True)
+ embed.add_field(name="Removes Roles", value=process_roles(cmd['rroles']), inline=True)
+ embed.add_field(name="Role Restrictions", value=process_roles(cmd['proles']), inline=True)
+
+ await ctx.send(embed=embed)
+
@ccrole.command(name="list")
async def ccrole_list(self, ctx):
"""Shows custom commands list"""
guild = ctx.guild
- commands = await self.config.guild(ctx.guild).cmdlist()
-
- if not commands:
- await ctx.send("There are no custom commands in this server. Use `{}ccrole add` to start adding some.".format(ctx.prefix))
+ cmd_list = await self.config.guild(guild).cmdlist()
+ cmd_list = {k: v for k, v in cmd_list.items() if v}
+ if not cmd_list:
+ await ctx.send(
+ "There are no custom commands in this server. Use `{}ccrole add` to start adding some.".format(
+ ctx.prefix))
return
- commands = ", ".join([ctx.prefix + c for c in sorted(commands.keys())])
- commands = "Custom commands:\n\n" + commands
+ cmd_list = ", ".join([ctx.prefix + c for c in sorted(cmd_list.keys())])
+ cmd_list = "Custom commands:\n\n" + cmd_list
- if len(commands) < 1500:
- await ctx.send(box(commands))
+ if len(cmd_list) < 1500: # I'm allowed to have arbitrary numbers for when it's too much to dm dammit
+ await ctx.send(box(cmd_list))
else:
- for page in pagify(commands, delims=[" ", "\n"]):
+ for page in pagify(cmd_list, delims=[" ", "\n"]):
await ctx.author.send(box(page))
await ctx.send("Command list DM'd")
@@ -177,24 +221,22 @@ class CCRole:
except ValueError:
return
-
cmdlist = self.config.guild(guild).cmdlist
- cmd = message.content[len(prefix):].split()[0]
- cmd = await cmdlist.get_raw(cmd.lower(), default=None)
-
- if cmd:
+ cmd = message.content[len(prefix):].split()[0].lower()
+ cmd = await cmdlist.get_raw(cmd, default=None)
+
+ if cmd is not None:
await self.eval_cc(cmd, message)
-
+
async def _get_roles_from_content(self, ctx, content):
content_list = content.split(",")
- role_list = []
try:
role_list = [discord.utils.get(ctx.guild.roles, name=role.strip(' ')).id for role in content_list]
- except:
+ except (discord.HTTPException, AttributeError): # None.id is attribute error
return None
else:
return role_list
-
+
async def get_prefix(self, message: discord.Message) -> str:
"""
Borrowed from alias cog
@@ -214,27 +256,26 @@ class CCRole:
if content.startswith(p):
return p
raise ValueError
-
+
async def eval_cc(self, cmd, message):
"""Does all the work"""
if cmd['proles'] and not (set(role.id for role in message.author.roles) & set(cmd['proles'])):
return # Not authorized, do nothing
-
+
if cmd['targeted']:
try:
target = discord.utils.get(message.guild.members, mention=message.content.split()[1])
- except:
+ except IndexError: # .split() return list of len<2
target = None
-
+
if not target:
- out_message = "This command is targeted! @mention a target\n`{} `".format(message.content.split()[0])
-
+ out_message = "This custom command is targeted! @mention a target\n`{} `".format(
+ message.content.split()[0])
await message.channel.send(out_message)
-
return
else:
target = message.author
-
+
if cmd['aroles']:
arole_list = [discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd['aroles']]
# await self.bot.send_message(message.channel, "Adding: "+str([str(arole) for arole in arole_list]))
@@ -243,7 +284,7 @@ class CCRole:
except discord.Forbidden:
await message.channel.send("Permission error: Unable to add roles")
await asyncio.sleep(1)
-
+
if cmd['rroles']:
rrole_list = [discord.utils.get(message.guild.roles, id=roleid) for roleid in cmd['rroles']]
# await self.bot.send_message(message.channel, "Removing: "+str([str(rrole) for rrole in rrole_list]))
@@ -251,37 +292,40 @@ class CCRole:
await target.remove_roles(*rrole_list)
except discord.Forbidden:
await message.channel.send("Permission error: Unable to remove roles")
- await message.channel.send(cmd['text'])
-
- # {'text': text, 'aroles': arole_list, 'rroles': rrole_list, "proles", prole_list, "targeted": targeted}
-
- # def format_cc(self, command, message):
- # results = re.findall("\{([^}]+)\}", command)
- # for result in results:
- # param = self.transform_parameter(result, message)
- # command = command.replace("{" + result + "}", param)
- # return command
-
- # def transform_parameter(self, result, message):
- # """
- # For security reasons only specific objects are allowed
- # Internals are ignored
- # """
- # raw_result = "{" + result + "}"
- # objects = {
- # "message" : message,
- # "author" : message.author,
- # "channel" : message.channel,
- # "server" : message.server
- # }
- # if result in objects:
- # return str(objects[result])
- # try:
- # first, second = result.split(".")
- # except ValueError:
- # return raw_result
- # if first in objects and not second.startswith("_"):
- # first = objects[first]
- # else:
- # return raw_result
- # return str(getattr(first, second, raw_result))
\ No newline at end of file
+
+ out_message = self.format_cc(cmd, message, target)
+ await message.channel.send(out_message)
+
+ def format_cc(self, cmd, message, target):
+ out = cmd['text']
+ results = re.findall("{([^}]+)\}", out)
+ for result in results:
+ param = self.transform_parameter(result, message, target)
+ out = out.replace("{" + result + "}", param)
+ return out
+
+ def transform_parameter(self, result, message, target):
+ """
+ For security reasons only specific objects are allowed
+ Internals are ignored
+ """
+ raw_result = "{" + result + "}"
+ objects = {
+ "message": message,
+ "author": message.author,
+ "channel": message.channel,
+ "server": message.guild,
+ "guild": message.guild,
+ "target": target
+ }
+ if result in objects:
+ return str(objects[result])
+ try:
+ first, second = result.split(".")
+ except ValueError:
+ return raw_result
+ if first in objects and not second.startswith("_"):
+ first = objects[first]
+ else:
+ return raw_result
+ return str(getattr(first, second, raw_result))
diff --git a/ccrole/info.json b/ccrole/info.json
index 73a1f79..0c0c70c 100644
--- a/ccrole/info.json
+++ b/ccrole/info.json
@@ -1,10 +1,22 @@
{
- "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.",
- "requirements" : [],
- "short" : "[Incomplete] Creates commands that adjust roles",
- "tags" : ["fox", "bobloy", "utility", "tools", "roles"]
+ "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. Get started with `[p]load ccrole` and `[p]help CCRole`",
+ "requirements": [],
+ "short": "[Incomplete] Creates commands that adjust roles",
+ "tags": [
+ "fox",
+ "bobloy",
+ "utility",
+ "tools",
+ "roles"
+ ]
}
\ No newline at end of file
diff --git a/chatter/__init__.py b/chatter/__init__.py
index 2d7a8e8..cc101b7 100644
--- a/chatter/__init__.py
+++ b/chatter/__init__.py
@@ -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'
+)
diff --git a/chatter/chat.py b/chatter/chat.py
new file mode 100644
index 0000000..8eb25d2
--- /dev/null
+++ b/chatter/chat.py
@@ -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:')
diff --git a/chatter/chatter.py b/chatter/chatter.py
deleted file mode 100644
index f6999fe..0000000
--- a/chatter/chatter.py
+++ /dev/null
@@ -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)
-
-
diff --git a/chatter/source/__init__.py b/chatter/chatterbot/__init__.py
similarity index 91%
rename from chatter/source/__init__.py
rename to chatter/chatterbot/__init__.py
index 2ea55f6..7a127ee 100644
--- a/chatter/source/__init__.py
+++ b/chatter/chatterbot/__init__.py
@@ -3,7 +3,7 @@ ChatterBot is a machine learning, conversational dialog engine.
"""
from .chatterbot import ChatBot
-__version__ = '0.8.4'
+__version__ = '0.8.5'
__author__ = 'Gunther Cox'
__email__ = 'gunthercx@gmail.com'
__url__ = 'https://github.com/gunthercox/ChatterBot'
diff --git a/chatter/source/__main__.py b/chatter/chatterbot/__main__.py
similarity index 99%
rename from chatter/source/__main__.py
rename to chatter/chatterbot/__main__.py
index a27f483..0322854 100644
--- a/chatter/source/__main__.py
+++ b/chatter/chatterbot/__main__.py
@@ -1,6 +1,5 @@
import sys
-
if __name__ == '__main__':
import importlib
diff --git a/chatter/source/adapters.py b/chatter/chatterbot/adapters.py
similarity index 96%
rename from chatter/source/adapters.py
rename to chatter/chatterbot/adapters.py
index f99734d..83ce94c 100644
--- a/chatter/source/adapters.py
+++ b/chatter/chatterbot/adapters.py
@@ -16,7 +16,7 @@ class Adapter(object):
"""
Gives the adapter access to an instance of the ChatBot class.
- :param chatbot: A chat bot instanse.
+ :param chatbot: A chat bot instance.
:type chatbot: ChatBot
"""
self.chatbot = chatbot
diff --git a/chatter/source/chatterbot.py b/chatter/chatterbot/chatterbot.py
similarity index 85%
rename from chatter/source/chatterbot.py
rename to chatter/chatterbot/chatterbot.py
index 66a92b9..08576c3 100644
--- a/chatter/source/chatterbot.py
+++ b/chatter/chatterbot/chatterbot.py
@@ -1,9 +1,8 @@
from __future__ import unicode_literals
+
import logging
-from .storage import StorageAdapter
-from .input import InputAdapter
-from .output import OutputAdapter
-from . import utils
+
+from chatter.chatterbot import utils
class ChatBot(object):
@@ -12,7 +11,7 @@ class ChatBot(object):
"""
def __init__(self, name, **kwargs):
- from .logic import MultiLogicAdapter
+ from chatter.chatterbot.logic import MultiLogicAdapter
self.name = name
kwargs['name'] = name
@@ -20,20 +19,20 @@ class ChatBot(object):
self.default_session = None
- storage_adapter = kwargs.get('storage_adapter', 'chatter.source.storage.SQLStorageAdapter')
+ storage_adapter = kwargs.get('storage_adapter', 'chatter.chatterbot.storage.SQLStorageAdapter')
logic_adapters = kwargs.get('logic_adapters', [
- 'chatter.source.logic.BestMatch'
+ 'chatter.chatterbot.logic.BestMatch'
])
- input_adapter = kwargs.get('input_adapter', 'chatter.source.input.VariableInputTypeAdapter')
+ input_adapter = kwargs.get('input_adapter', 'chatter.chatterbot.input.VariableInputTypeAdapter')
- output_adapter = kwargs.get('output_adapter', 'chatter.source.output.OutputAdapter')
+ output_adapter = kwargs.get('output_adapter', 'chatter.chatterbot.output.OutputAdapter')
# Check that each adapter is a valid subclass of it's respective parent
- utils.validate_adapter_class(storage_adapter, StorageAdapter)
- utils.validate_adapter_class(input_adapter, InputAdapter)
- utils.validate_adapter_class(output_adapter, OutputAdapter)
+ # utils.validate_adapter_class(storage_adapter, StorageAdapter)
+ # utils.validate_adapter_class(input_adapter, InputAdapter)
+ # utils.validate_adapter_class(output_adapter, OutputAdapter)
self.logic = MultiLogicAdapter(**kwargs)
self.storage = utils.initialize_class(storage_adapter, **kwargs)
@@ -45,7 +44,7 @@ class ChatBot(object):
# Add required system logic adapter
self.logic.system_adapters.append(
- utils.initialize_class('chatter.source.logic.NoKnowledgeAdapter', **kwargs)
+ utils.initialize_class('chatter.chatterbot.logic.NoKnowledgeAdapter', **kwargs)
)
for adapter in logic_adapters:
@@ -59,7 +58,7 @@ class ChatBot(object):
preprocessors = kwargs.get(
'preprocessors', [
- 'chatter.source.preprocessors.clean_whitespace'
+ 'chatter.chatterbot.preprocessors.clean_whitespace'
]
)
@@ -69,7 +68,7 @@ class ChatBot(object):
self.preprocessors.append(utils.import_module(preprocessor))
# Use specified trainer or fall back to the default
- trainer = kwargs.get('trainer', 'chatter.source.trainers.Trainer')
+ trainer = kwargs.get('trainer', 'chatter.chatterbot.trainers.Trainer')
TrainerClass = utils.import_module(trainer)
self.trainer = TrainerClass(self.storage, **kwargs)
self.training_data = kwargs.get('training_data')
@@ -137,7 +136,7 @@ class ChatBot(object):
"""
Learn that the statement provided is a valid response.
"""
- from .conversation import Response
+ from chatter.chatterbot.conversation import Response
if previous_statement:
statement.add_response(
diff --git a/chatter/source/comparisons.py b/chatter/chatterbot/comparisons.py
similarity index 94%
rename from chatter/source/comparisons.py
rename to chatter/chatterbot/comparisons.py
index 816e175..5e253a0 100644
--- a/chatter/source/comparisons.py
+++ b/chatter/chatterbot/comparisons.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
-import sys
"""
@@ -58,19 +57,14 @@ class LevenshteinDistance(Comparator):
:rtype: float
"""
- PYTHON = sys.version_info[0]
-
# Return 0 if either statement has a falsy text value
if not statement.text or not other_statement.text:
return 0
# Get the lowercase version of both strings
- if PYTHON < 3:
- statement_text = unicode(statement.text.lower()) # NOQA
- other_statement_text = unicode(other_statement.text.lower()) # NOQA
- else:
- statement_text = str(statement.text.lower())
- other_statement_text = str(other_statement.text.lower())
+
+ statement_text = str(statement.text.lower())
+ other_statement_text = str(other_statement.text.lower())
similarity = SequenceMatcher(
None,
@@ -98,7 +92,7 @@ class SynsetDistance(Comparator):
"""
Download required NLTK corpora if they have not already been downloaded.
"""
- from .utils import nltk_download_corpus
+ from chatter.chatterbot.utils import nltk_download_corpus
nltk_download_corpus('corpora/wordnet')
@@ -106,7 +100,7 @@ class SynsetDistance(Comparator):
"""
Download required NLTK corpora if they have not already been downloaded.
"""
- from .utils import nltk_download_corpus
+ from chatter.chatterbot.utils import nltk_download_corpus
nltk_download_corpus('tokenizers/punkt')
@@ -114,7 +108,7 @@ class SynsetDistance(Comparator):
"""
Download required NLTK corpora if they have not already been downloaded.
"""
- from .utils import nltk_download_corpus
+ from chatter.chatterbot.utils import nltk_download_corpus
nltk_download_corpus('corpora/stopwords')
@@ -130,7 +124,7 @@ class SynsetDistance(Comparator):
"""
from nltk.corpus import wordnet
from nltk import word_tokenize
- from . import utils
+ from chatter.chatterbot import utils
import itertools
tokens1 = word_tokenize(statement.text.lower())
@@ -183,7 +177,7 @@ class SentimentComparison(Comparator):
Download the NLTK vader lexicon for sentiment analysis
that is required for this algorithm to run.
"""
- from .utils import nltk_download_corpus
+ from chatter.chatterbot.utils import nltk_download_corpus
nltk_download_corpus('sentiment/vader_lexicon')
@@ -258,7 +252,7 @@ class JaccardSimilarity(Comparator):
Download the NLTK wordnet corpora that is required for this algorithm
to run only if the corpora has not already been downloaded.
"""
- from .utils import nltk_download_corpus
+ from chatter.chatterbot.utils import nltk_download_corpus
nltk_download_corpus('corpora/wordnet')
diff --git a/chatter/source/constants.py b/chatter/chatterbot/constants.py
similarity index 100%
rename from chatter/source/constants.py
rename to chatter/chatterbot/constants.py
diff --git a/chatter/source/conversation.py b/chatter/chatterbot/conversation.py
similarity index 92%
rename from chatter/source/conversation.py
rename to chatter/chatterbot/conversation.py
index ea674aa..52231f8 100644
--- a/chatter/source/conversation.py
+++ b/chatter/chatterbot/conversation.py
@@ -3,6 +3,7 @@ class StatementMixin(object):
This class has shared methods used to
normalize different statement models.
"""
+ tags = []
def get_tags(self):
"""
@@ -25,7 +26,6 @@ class Statement(StatementMixin):
"""
def __init__(self, text, **kwargs):
- import sys
# Try not to allow non-string types to be passed to statements
try:
@@ -33,13 +33,6 @@ class Statement(StatementMixin):
except UnicodeEncodeError:
pass
- # Prefer decoded utf8-strings in Python 2.7
- if sys.version_info[0] < 3:
- try:
- text = text.decode('utf-8')
- except UnicodeEncodeError:
- pass
-
self.text = text
self.tags = kwargs.pop('tags', [])
self.in_response_to = kwargs.pop('in_response_to', [])
@@ -156,11 +149,7 @@ class Statement(StatementMixin):
:returns: A dictionary representation of the statement object.
:rtype: dict
"""
- data = {}
-
- data['text'] = self.text
- data['in_response_to'] = []
- data['extra_data'] = self.extra_data
+ data = {'text': self.text, 'in_response_to': [], 'extra_data': self.extra_data}
for response in self.in_response_to:
data['in_response_to'].append(response.serialize())
@@ -219,11 +208,6 @@ class Response(object):
return self.text == other
def serialize(self):
- data = {}
-
- data['text'] = self.text
- data['created_at'] = self.created_at.isoformat()
-
- data['occurrence'] = self.occurrence
+ data = {'text': self.text, 'created_at': self.created_at.isoformat(), 'occurrence': self.occurrence}
return data
diff --git a/chatter/source/corpus.py b/chatter/chatterbot/corpus.py
similarity index 99%
rename from chatter/source/corpus.py
rename to chatter/chatterbot/corpus.py
index 65da8eb..4bf0e4b 100644
--- a/chatter/source/corpus.py
+++ b/chatter/chatterbot/corpus.py
@@ -5,7 +5,6 @@ View the corpus on GitHub at https://github.com/gunthercox/chatterbot-corpus
from chatterbot_corpus import Corpus
-
__all__ = (
'Corpus',
)
diff --git a/chatter/source/ext/__init__.py b/chatter/chatterbot/ext/__init__.py
similarity index 100%
rename from chatter/source/ext/__init__.py
rename to chatter/chatterbot/ext/__init__.py
diff --git a/chatter/source/ext/django_chatterbot/management/__init__.py b/chatter/chatterbot/ext/sqlalchemy_app/__init__.py
similarity index 100%
rename from chatter/source/ext/django_chatterbot/management/__init__.py
rename to chatter/chatterbot/ext/sqlalchemy_app/__init__.py
diff --git a/chatter/source/ext/sqlalchemy_app/models.py b/chatter/chatterbot/ext/sqlalchemy_app/models.py
similarity index 88%
rename from chatter/source/ext/sqlalchemy_app/models.py
rename to chatter/chatterbot/ext/sqlalchemy_app/models.py
index 9f1b0d3..6a7dc00 100644
--- a/chatter/source/ext/sqlalchemy_app/models.py
+++ b/chatter/chatterbot/ext/sqlalchemy_app/models.py
@@ -1,11 +1,11 @@
from sqlalchemy import Table, Column, Integer, DateTime, ForeignKey, PickleType
+from sqlalchemy.ext.declarative import declared_attr, declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
-from sqlalchemy.ext.declarative import declared_attr, declarative_base
-from ...constants import TAG_NAME_MAX_LENGTH, STATEMENT_TEXT_MAX_LENGTH
-from .types import UnicodeString
-from ...conversation import StatementMixin
+from chatter.chatterbot.constants import TAG_NAME_MAX_LENGTH, STATEMENT_TEXT_MAX_LENGTH
+from chatter.chatterbot.conversation import StatementMixin
+from chatter.chatterbot.ext.sqlalchemy_app.types import UnicodeString
class ModelBase(object):
@@ -29,7 +29,6 @@ class ModelBase(object):
Base = declarative_base(cls=ModelBase)
-
tag_association_table = Table(
'tag_association',
Base.metadata,
@@ -73,8 +72,8 @@ class Statement(Base, StatementMixin):
return [tag.name for tag in self.tags]
def get_statement(self):
- from ...conversation import Statement as StatementObject
- from ...conversation import Response as ResponseObject
+ from chatter.chatterbot.conversation import Statement as StatementObject
+ from chatter.chatterbot.conversation import Response as ResponseObject
statement = StatementObject(
self.text,
diff --git a/chatter/source/ext/sqlalchemy_app/types.py b/chatter/chatterbot/ext/sqlalchemy_app/types.py
similarity index 70%
rename from chatter/source/ext/sqlalchemy_app/types.py
rename to chatter/chatterbot/ext/sqlalchemy_app/types.py
index b48f4f6..ee9b123 100644
--- a/chatter/source/ext/sqlalchemy_app/types.py
+++ b/chatter/chatterbot/ext/sqlalchemy_app/types.py
@@ -13,9 +13,4 @@ class UnicodeString(TypeDecorator):
Coerce Python bytestrings to unicode before
saving them to the database.
"""
- import sys
-
- if sys.version_info[0] < 3:
- if isinstance(value, str):
- value = value.decode('utf-8')
return value
diff --git a/chatter/source/filters.py b/chatter/chatterbot/filters.py
similarity index 100%
rename from chatter/source/filters.py
rename to chatter/chatterbot/filters.py
diff --git a/chatter/source/input/__init__.py b/chatter/chatterbot/input/__init__.py
similarity index 99%
rename from chatter/source/input/__init__.py
rename to chatter/chatterbot/input/__init__.py
index 34d9568..53c53f9 100644
--- a/chatter/source/input/__init__.py
+++ b/chatter/chatterbot/input/__init__.py
@@ -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',
diff --git a/chatter/source/input/gitter.py b/chatter/chatterbot/input/gitter.py
similarity index 98%
rename from chatter/source/input/gitter.py
rename to chatter/chatterbot/input/gitter.py
index 6ed83db..9018e37 100644
--- a/chatter/source/input/gitter.py
+++ b/chatter/chatterbot/input/gitter.py
@@ -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):
diff --git a/chatter/source/input/hipchat.py b/chatter/chatterbot/input/hipchat.py
similarity index 96%
rename from chatter/source/input/hipchat.py
rename to chatter/chatterbot/input/hipchat.py
index b251157..b5da731 100644
--- a/chatter/source/input/hipchat.py
+++ b/chatter/chatterbot/input/hipchat.py
@@ -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):
diff --git a/chatter/source/input/input_adapter.py b/chatter/chatterbot/input/input_adapter.py
similarity index 95%
rename from chatter/source/input/input_adapter.py
rename to chatter/chatterbot/input/input_adapter.py
index 3bc4b08..49c63db 100644
--- a/chatter/source/input/input_adapter.py
+++ b/chatter/chatterbot/input/input_adapter.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from ..adapters import Adapter
+
+from chatter.chatterbot.adapters import Adapter
class InputAdapter(Adapter):
diff --git a/chatter/source/input/mailgun.py b/chatter/chatterbot/input/mailgun.py
similarity index 93%
rename from chatter/source/input/mailgun.py
rename to chatter/chatterbot/input/mailgun.py
index b1fe705..6fb78a8 100644
--- a/chatter/source/input/mailgun.py
+++ b/chatter/chatterbot/input/mailgun.py
@@ -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):
diff --git a/chatter/source/input/microsoft.py b/chatter/chatterbot/input/microsoft.py
similarity index 92%
rename from chatter/source/input/microsoft.py
rename to chatter/chatterbot/input/microsoft.py
index 395a3de..3a255bf 100644
--- a/chatter/source/input/microsoft.py
+++ b/chatter/chatterbot/input/microsoft.py
@@ -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):
@@ -21,10 +23,10 @@ class Microsoft(InputAdapter):
# NOTE: Direct Line client credentials are different from your bot's
# credentials
- self.direct_line_token_or_secret = kwargs.\
+ self.direct_line_token_or_secret = kwargs. \
get('direct_line_token_or_secret')
- authorization_header = 'BotConnector {}'.\
+ authorization_header = 'BotConnector {}'. \
format(self.direct_line_token_or_secret)
self.headers = {
@@ -62,7 +64,7 @@ class Microsoft(InputAdapter):
def get_most_recent_message(self):
import requests
- endpoint = '{host}/api/conversations/{id}/messages'\
+ endpoint = '{host}/api/conversations/{id}/messages' \
.format(host=self.directline_host,
id=self.conversation_id)
diff --git a/chatter/source/input/terminal.py b/chatter/chatterbot/input/terminal.py
similarity index 70%
rename from chatter/source/input/terminal.py
rename to chatter/chatterbot/input/terminal.py
index e2d7ba2..20cb3c2 100644
--- a/chatter/source/input/terminal.py
+++ b/chatter/chatterbot/input/terminal.py
@@ -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):
diff --git a/chatter/source/input/variable_input_type_adapter.py b/chatter/chatterbot/input/variable_input_type_adapter.py
similarity index 86%
rename from chatter/source/input/variable_input_type_adapter.py
rename to chatter/chatterbot/input/variable_input_type_adapter.py
index 9158611..d2d598c 100644
--- a/chatter/source/input/variable_input_type_adapter.py
+++ b/chatter/chatterbot/input/variable_input_type_adapter.py
@@ -1,22 +1,18 @@
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, )
+ 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
+ string_types = str
if hasattr(statement, 'text'):
return self.OBJECT
diff --git a/chatter/source/logic/__init__.py b/chatter/chatterbot/logic/__init__.py
similarity index 99%
rename from chatter/source/logic/__init__.py
rename to chatter/chatterbot/logic/__init__.py
index ecb1020..8a6cc97 100644
--- a/chatter/source/logic/__init__.py
+++ b/chatter/chatterbot/logic/__init__.py
@@ -7,7 +7,6 @@ from .no_knowledge_adapter import NoKnowledgeAdapter
from .specific_response import SpecificResponseAdapter
from .time_adapter import TimeLogicAdapter
-
__all__ = (
'LogicAdapter',
'BestMatch',
diff --git a/chatter/source/logic/best_match.py b/chatter/chatterbot/logic/best_match.py
similarity index 98%
rename from chatter/source/logic/best_match.py
rename to chatter/chatterbot/logic/best_match.py
index 712c8f9..f19fc99 100644
--- a/chatter/source/logic/best_match.py
+++ b/chatter/chatterbot/logic/best_match.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from .logic_adapter import LogicAdapter
+
+from chatter.chatterbot.logic import LogicAdapter
class BestMatch(LogicAdapter):
diff --git a/chatter/source/logic/logic_adapter.py b/chatter/chatterbot/logic/logic_adapter.py
similarity index 93%
rename from chatter/source/logic/logic_adapter.py
rename to chatter/chatterbot/logic/logic_adapter.py
index df2c143..1239cca 100644
--- a/chatter/source/logic/logic_adapter.py
+++ b/chatter/chatterbot/logic/logic_adapter.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
-from ..adapters import Adapter
-from ..utils import import_module
+
+from chatter.chatterbot.adapters import Adapter
+from chatter.chatterbot.utils import import_module
class LogicAdapter(Adapter):
@@ -17,8 +18,8 @@ class LogicAdapter(Adapter):
def __init__(self, **kwargs):
super(LogicAdapter, self).__init__(**kwargs)
- from ..comparisons import levenshtein_distance
- from ..response_selection import get_first_response
+ from chatter.chatterbot.comparisons import levenshtein_distance
+ from chatter.chatterbot.response_selection import get_first_response
# Import string module parameters
if 'statement_comparison_function' in kwargs:
diff --git a/chatter/source/logic/low_confidence.py b/chatter/chatterbot/logic/low_confidence.py
similarity index 94%
rename from chatter/source/logic/low_confidence.py
rename to chatter/chatterbot/logic/low_confidence.py
index fb5435c..2d33bba 100644
--- a/chatter/source/logic/low_confidence.py
+++ b/chatter/chatterbot/logic/low_confidence.py
@@ -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):
diff --git a/chatter/source/logic/mathematical_evaluation.py b/chatter/chatterbot/logic/mathematical_evaluation.py
similarity index 95%
rename from chatter/source/logic/mathematical_evaluation.py
rename to chatter/chatterbot/logic/mathematical_evaluation.py
index 2a65fdc..af27548 100644
--- a/chatter/source/logic/mathematical_evaluation.py
+++ b/chatter/chatterbot/logic/mathematical_evaluation.py
@@ -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):
diff --git a/chatter/source/logic/multi_adapter.py b/chatter/chatterbot/logic/multi_adapter.py
similarity index 95%
rename from chatter/source/logic/multi_adapter.py
rename to chatter/chatterbot/logic/multi_adapter.py
index 150f6c3..5ae79f4 100644
--- a/chatter/source/logic/multi_adapter.py
+++ b/chatter/chatterbot/logic/multi_adapter.py
@@ -1,7 +1,9 @@
from __future__ import unicode_literals
+
from collections import Counter
-from .. import utils
-from .logic_adapter import LogicAdapter
+
+from chatter.chatterbot import utils
+from chatter.chatterbot.logic import LogicAdapter
class MultiLogicAdapter(LogicAdapter):
@@ -13,7 +15,7 @@ class MultiLogicAdapter(LogicAdapter):
"""
def __init__(self, **kwargs):
- super().__init__(**kwargs)
+ super(MultiLogicAdapter, self).__init__(**kwargs)
# Logic adapters added by the chat bot
self.adapters = []
@@ -49,7 +51,7 @@ class MultiLogicAdapter(LogicAdapter):
if adapter.can_process(statement):
output = adapter.process(statement)
- results.append((output.confidence, output, ))
+ results.append((output.confidence, output,))
self.logger.info(
'{} selected "{}" as a response with a confidence of {}'.format(
diff --git a/chatter/source/logic/no_knowledge_adapter.py b/chatter/chatterbot/logic/no_knowledge_adapter.py
similarity index 93%
rename from chatter/source/logic/no_knowledge_adapter.py
rename to chatter/chatterbot/logic/no_knowledge_adapter.py
index 59b11fd..848b23e 100644
--- a/chatter/source/logic/no_knowledge_adapter.py
+++ b/chatter/chatterbot/logic/no_knowledge_adapter.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from .logic_adapter import LogicAdapter
+
+from chatter.chatterbot.logic import LogicAdapter
class NoKnowledgeAdapter(LogicAdapter):
diff --git a/chatter/source/logic/specific_response.py b/chatter/chatterbot/logic/specific_response.py
similarity index 89%
rename from chatter/source/logic/specific_response.py
rename to chatter/chatterbot/logic/specific_response.py
index 2ed6da1..ef7a630 100644
--- a/chatter/source/logic/specific_response.py
+++ b/chatter/chatterbot/logic/specific_response.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from .logic_adapter import LogicAdapter
+
+from chatter.chatterbot.logic import LogicAdapter
class SpecificResponseAdapter(LogicAdapter):
@@ -15,7 +16,7 @@ class SpecificResponseAdapter(LogicAdapter):
def __init__(self, **kwargs):
super(SpecificResponseAdapter, self).__init__(**kwargs)
- from ..conversation import Statement
+ from chatter.chatterbot.conversation import Statement
self.input_text = kwargs.get('input_text')
diff --git a/chatter/source/logic/time_adapter.py b/chatter/chatterbot/logic/time_adapter.py
similarity index 92%
rename from chatter/source/logic/time_adapter.py
rename to chatter/chatterbot/logic/time_adapter.py
index 3de4001..d4bbd15 100644
--- a/chatter/source/logic/time_adapter.py
+++ b/chatter/chatterbot/logic/time_adapter.py
@@ -1,6 +1,8 @@
from __future__ import unicode_literals
+
from datetime import datetime
-from .logic_adapter import LogicAdapter
+
+from chatter.chatterbot.logic import LogicAdapter
class TimeLogicAdapter(LogicAdapter):
@@ -40,8 +42,8 @@ class TimeLogicAdapter(LogicAdapter):
])
labeled_data = (
- [(name, 0) for name in self.negative] +
- [(name, 1) for name in self.positive]
+ [(name, 0) for name in self.negative] +
+ [(name, 1) for name in self.positive]
)
train_set = [
@@ -79,7 +81,7 @@ class TimeLogicAdapter(LogicAdapter):
return features
def process(self, statement):
- from ..conversation import Statement
+ from chatter.chatterbot.conversation import Statement
now = datetime.now()
diff --git a/chatter/source/output/__init__.py b/chatter/chatterbot/output/__init__.py
similarity index 100%
rename from chatter/source/output/__init__.py
rename to chatter/chatterbot/output/__init__.py
index 0d64ca4..52c3534 100644
--- a/chatter/source/output/__init__.py
+++ b/chatter/chatterbot/output/__init__.py
@@ -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',
diff --git a/chatter/source/output/gitter.py b/chatter/chatterbot/output/gitter.py
similarity index 97%
rename from chatter/source/output/gitter.py
rename to chatter/chatterbot/output/gitter.py
index db654e2..664d341 100644
--- a/chatter/source/output/gitter.py
+++ b/chatter/chatterbot/output/gitter.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from .output_adapter import OutputAdapter
+
+from chatter.chatterbot.output import OutputAdapter
class Gitter(OutputAdapter):
diff --git a/chatter/source/output/hipchat.py b/chatter/chatterbot/output/hipchat.py
similarity index 97%
rename from chatter/source/output/hipchat.py
rename to chatter/chatterbot/output/hipchat.py
index 4eaa9a7..20029fa 100644
--- a/chatter/source/output/hipchat.py
+++ b/chatter/chatterbot/output/hipchat.py
@@ -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):
diff --git a/chatter/source/output/mailgun.py b/chatter/chatterbot/output/mailgun.py
similarity index 96%
rename from chatter/source/output/mailgun.py
rename to chatter/chatterbot/output/mailgun.py
index 6bb4954..d022a51 100644
--- a/chatter/source/output/mailgun.py
+++ b/chatter/chatterbot/output/mailgun.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from .output_adapter import OutputAdapter
+
+from chatter.chatterbot.output import OutputAdapter
class Mailgun(OutputAdapter):
diff --git a/chatter/source/output/microsoft.py b/chatter/chatterbot/output/microsoft.py
similarity index 98%
rename from chatter/source/output/microsoft.py
rename to chatter/chatterbot/output/microsoft.py
index 177dc35..4f2426a 100644
--- a/chatter/source/output/microsoft.py
+++ b/chatter/chatterbot/output/microsoft.py
@@ -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):
diff --git a/chatter/source/output/output_adapter.py b/chatter/chatterbot/output/output_adapter.py
similarity index 92%
rename from chatter/source/output/output_adapter.py
rename to chatter/chatterbot/output/output_adapter.py
index 880cb18..5d13dd7 100644
--- a/chatter/source/output/output_adapter.py
+++ b/chatter/chatterbot/output/output_adapter.py
@@ -1,4 +1,4 @@
-from ..adapters import Adapter
+from chatter.chatterbot.adapters import Adapter
class OutputAdapter(Adapter):
diff --git a/chatter/source/output/terminal.py b/chatter/chatterbot/output/terminal.py
similarity index 87%
rename from chatter/source/output/terminal.py
rename to chatter/chatterbot/output/terminal.py
index f189aba..005d0ae 100644
--- a/chatter/source/output/terminal.py
+++ b/chatter/chatterbot/output/terminal.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
-from .output_adapter import OutputAdapter
+
+from chatter.chatterbot.output import OutputAdapter
class TerminalAdapter(OutputAdapter):
diff --git a/chatter/source/parsing.py b/chatter/chatterbot/parsing.py
similarity index 99%
rename from chatter/source/parsing.py
rename to chatter/chatterbot/parsing.py
index cf955ff..5aafa75 100644
--- a/chatter/source/parsing.py
+++ b/chatter/chatterbot/parsing.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
+import calendar
import re
from datetime import timedelta, datetime
-import calendar
# Variations of dates that the parser can capture
year_variations = ['year', 'years', 'yrs']
@@ -611,6 +611,7 @@ def date_from_duration(base_date, number_as_string, unit, duration, base_time=No
if base_time is not None:
base_date = date_from_adverb(base_date, base_time)
num = convert_string_to_number(number_as_string)
+ args = {}
if unit in day_variations:
args = {'days': num}
elif unit in minute_variations:
diff --git a/chatter/source/preprocessors.py b/chatter/chatterbot/preprocessors.py
similarity index 80%
rename from chatter/source/preprocessors.py
rename to chatter/chatterbot/preprocessors.py
index f7043b1..2ab0ee0 100644
--- a/chatter/source/preprocessors.py
+++ b/chatter/chatterbot/preprocessors.py
@@ -27,14 +27,9 @@ def unescape_html(chatbot, statement):
Convert escaped html characters into unescaped html characters.
For example: "<b>" becomes "".
"""
- import sys
# Replace HTML escape characters
- if sys.version_info[0] < 3:
- from HTMLParser import HTMLParser
- html = HTMLParser()
- else:
- import html
+ import html
statement.text = html.unescape(statement.text)
@@ -47,11 +42,6 @@ def convert_to_ascii(chatbot, statement):
For example: "på fédéral" becomes "pa federal".
"""
import unicodedata
- import sys
-
- # Normalize unicode characters
- if sys.version_info[0] < 3:
- statement.text = unicode(statement.text) # NOQA
text = unicodedata.normalize('NFKD', statement.text)
text = text.encode('ascii', 'ignore').decode('utf-8')
diff --git a/chatter/source/response_selection.py b/chatter/chatterbot/response_selection.py
similarity index 100%
rename from chatter/source/response_selection.py
rename to chatter/chatterbot/response_selection.py
diff --git a/chatter/source/storage/__init__.py b/chatter/chatterbot/storage/__init__.py
similarity index 73%
rename from chatter/source/storage/__init__.py
rename to chatter/chatterbot/storage/__init__.py
index 2b4e286..77d3e04 100644
--- a/chatter/source/storage/__init__.py
+++ b/chatter/chatterbot/storage/__init__.py
@@ -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',
)
diff --git a/chatter/source/storage/mongodb.py b/chatter/chatterbot/storage/mongodb.py
similarity index 97%
rename from chatter/source/storage/mongodb.py
rename to chatter/chatterbot/storage/mongodb.py
index 92ce5a1..1ddb625 100644
--- a/chatter/source/storage/mongodb.py
+++ b/chatter/chatterbot/storage/mongodb.py
@@ -1,10 +1,13 @@
-from . import StorageAdapter
+from chatter.chatterbot.storage import StorageAdapter
class Query(object):
- def __init__(self, query={}):
- self.query = query
+ def __init__(self, query=None):
+ if query is None:
+ self.query = {}
+ else:
+ self.query = query
def value(self):
return self.query.copy()
@@ -116,7 +119,7 @@ class MongoDatabaseAdapter(StorageAdapter):
"""
Return the class for the statement model.
"""
- from ..conversation import Statement
+ from chatter.chatterbot.conversation import Statement
# Create a storage-aware statement
statement = Statement
@@ -128,7 +131,7 @@ class MongoDatabaseAdapter(StorageAdapter):
"""
Return the class for the response model.
"""
- from ..conversation import Response
+ from chatter.chatterbot.conversation import Response
# Create a storage-aware response
response = Response
diff --git a/chatter/source/storage/sql_storage.py b/chatter/chatterbot/storage/sql_storage.py
similarity index 94%
rename from chatter/source/storage/sql_storage.py
rename to chatter/chatterbot/storage/sql_storage.py
index 21c84e6..23b54ef 100644
--- a/chatter/source/storage/sql_storage.py
+++ b/chatter/chatterbot/storage/sql_storage.py
@@ -1,8 +1,8 @@
-from . import StorageAdapter
+from chatter.chatterbot.storage import StorageAdapter
def get_response_table(response):
- from ..ext.sqlalchemy_app.models import Response
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Response
return Response(text=response.text, occurrence=response.occurrence)
@@ -86,28 +86,28 @@ class SQLStorageAdapter(StorageAdapter):
"""
Return the statement model.
"""
- from ..ext.sqlalchemy_app.models import Statement
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Statement
return Statement
def get_response_model(self):
"""
Return the response model.
"""
- from ..ext.sqlalchemy_app.models import Response
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Response
return Response
def get_conversation_model(self):
"""
Return the conversation model.
"""
- from ..ext.sqlalchemy_app.models import Conversation
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Conversation
return Conversation
def get_tag_model(self):
"""
Return the conversation model.
"""
- from ..ext.sqlalchemy_app.models import Tag
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Tag
return Tag
def count(self):
@@ -183,7 +183,7 @@ class SQLStorageAdapter(StorageAdapter):
if isinstance(_filter, list):
if len(_filter) == 0:
_query = _response_query.filter(
- Statement.in_response_to == None # NOQA Here must use == instead of is
+ Statement.in_response_to is None # NOQA Here must use == instead of is
)
else:
for f in _filter:
@@ -193,7 +193,7 @@ class SQLStorageAdapter(StorageAdapter):
if fp == 'in_response_to__contains':
_query = _response_query.join(Response).filter(Response.text == _filter)
else:
- _query = _response_query.filter(Statement.in_response_to == None) # NOQA
+ _query = _response_query.filter(Statement.in_response_to is None) # NOQA
else:
if _query:
_query = _query.filter(Response.statement_text.like('%' + _filter + '%'))
@@ -379,14 +379,14 @@ class SQLStorageAdapter(StorageAdapter):
"""
Drop the database attached to a given adapter.
"""
- from ..ext.sqlalchemy_app.models import Base
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Base
Base.metadata.drop_all(self.engine)
def create(self):
"""
Populate the database with the tables.
"""
- from ..ext.sqlalchemy_app.models import Base
+ from chatter.chatterbot.ext.sqlalchemy_app.models import Base
Base.metadata.create_all(self.engine)
def _session_finish(self, session, statement_text=None):
diff --git a/chatter/source/storage/storage_adapter.py b/chatter/chatterbot/storage/storage_adapter.py
similarity index 94%
rename from chatter/source/storage/storage_adapter.py
rename to chatter/chatterbot/storage/storage_adapter.py
index 50beac7..cf1f45b 100644
--- a/chatter/source/storage/storage_adapter.py
+++ b/chatter/chatterbot/storage/storage_adapter.py
@@ -24,12 +24,12 @@ class StorageAdapter(object):
# The string must be lowercase
model_name = model_name.lower()
- kwarg_model_key = '%s_model' % (model_name, )
+ kwarg_model_key = '%s_model' % (model_name,)
if kwarg_model_key in self.kwargs:
return self.kwargs.get(kwarg_model_key)
- get_model_method = getattr(self, 'get_%s_model' % (model_name, ))
+ get_model_method = getattr(self, 'get_%s_model' % (model_name,))
return get_model_method()
@@ -157,7 +157,10 @@ class StorageAdapter(object):
class EmptyDatabaseException(Exception):
- def __init__(self, value='The database currently contains no entries. At least one entry is expected. You may need to train your chat bot to populate your database.'):
+ def __init__(self,
+ value='The database currently contains no entries. '
+ 'At least one entry is expected. '
+ 'You may need to train your chat bot to populate your database.'):
self.value = value
def __str__(self):
diff --git a/chatter/source/trainers.py b/chatter/chatterbot/trainers.py
similarity index 95%
rename from chatter/source/trainers.py
rename to chatter/chatterbot/trainers.py
index 1f634d1..f3a4165 100644
--- a/chatter/source/trainers.py
+++ b/chatter/chatterbot/trainers.py
@@ -1,8 +1,9 @@
import logging
import os
import sys
-from .conversation import Statement, Response
-from . import utils
+
+from chatter.chatterbot import utils
+from chatter.chatterbot.conversation import Statement, Response
class Trainer(object):
@@ -60,8 +61,8 @@ class Trainer(object):
def __init__(self, value=None):
default = (
- 'A training class must be specified before calling train(). ' +
- 'See http://chatterbot.readthedocs.io/en/stable/training.html'
+ 'A training class must be specified before calling train(). ' +
+ 'See http://chatterbot.readthedocs.io/en/stable/training.html'
)
self.value = value or default
@@ -84,7 +85,7 @@ class Trainer(object):
import json
export = {'conversations': self._generate_export_data()}
with open(file_path, 'w+') as jsonfile:
- json.dump(export, jsonfile, ensure_ascii=False)
+ json.dump(export, jsonfile, ensure_ascii=True)
class ListTrainer(Trainer):
@@ -126,7 +127,7 @@ class ChatterBotCorpusTrainer(Trainer):
def __init__(self, storage, **kwargs):
super(ChatterBotCorpusTrainer, self).__init__(storage, **kwargs)
- from .corpus import Corpus
+ from chatter.chatterbot.corpus import Corpus
self.corpus = Corpus()
@@ -224,7 +225,7 @@ class TwitterTrainer(Trainer):
for word in tweet_words:
# If the word contains only letters with a length from 4 to 9
- if word.isalpha() and len(word) > 3 and len(word) <= 9:
+ if word.isalpha() and 3 < len(word) <= 9:
words.add(word)
return words
@@ -392,10 +393,9 @@ class UbuntuCorpusTrainer(Trainer):
file_kwargs = {}
- if sys.version_info[0] > 2:
- # Specify the encoding in Python versions 3 and up
- file_kwargs['encoding'] = 'utf-8'
- # WARNING: This might fail to read a unicode corpus file in Python 2.x
+ # Specify the encoding in Python versions 3 and up
+ file_kwargs['encoding'] = 'utf-8'
+ # WARNING: This might fail to read a unicode corpus file in Python 2.x
for file in glob.iglob(extracted_corpus_path):
self.logger.info('Training from: {}'.format(file))
diff --git a/chatter/source/utils.py b/chatter/chatterbot/utils.py
similarity index 93%
rename from chatter/source/utils.py
rename to chatter/chatterbot/utils.py
index 684d7f7..9785bd4 100644
--- a/chatter/source/utils.py
+++ b/chatter/chatterbot/utils.py
@@ -46,7 +46,7 @@ def validate_adapter_class(validate_class, adapter_class):
:raises: Adapter.InvalidAdapterTypeException
"""
- from .adapters import Adapter
+ from chatter.chatterbot.adapters import Adapter
# If a dictionary was passed in, check if it has an import_path attribute
if isinstance(validate_class, dict):
@@ -75,17 +75,8 @@ def input_function():
Normalizes reading input between python 2 and 3.
The function 'raw_input' becomes 'input' in Python 3.
"""
- import sys
-
- if sys.version_info[0] < 3:
- user_input = str(raw_input()) # NOQA
- # Avoid problems using format strings with unicode characters
- if user_input:
- user_input = user_input.decode('utf-8')
-
- else:
- user_input = input() # NOQA
+ user_input = input() # NOQA
return user_input
@@ -137,7 +128,7 @@ def remove_stopwords(tokens, language):
Stop words are words like "is, the, a, ..."
Be sure to download the required NLTK corpus before calling this function:
- - from chatterbot.utils import nltk_download_corpus
+ - from chatter.chatterbot.utils import nltk_download_corpus
- nltk_download_corpus('corpora/stopwords')
"""
from nltk.corpus import stopwords
diff --git a/chatter/info.json b/chatter/info.json
index bd4870a..7c8a9f3 100644
--- a/chatter/info.json
+++ b/chatter/info.json
@@ -1,10 +1,30 @@
{
- "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"],
- "short" : "Local Chatbot run on machine learning",
- "tags" : ["chat", "chatbot", "cleverbot", "clever","bobloy"]
+ "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! 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"
+ ]
}
\ No newline at end of file
diff --git a/chatter/source/__pycache__/__init__.cpython-36.pyc b/chatter/source/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index b9aa8f4..0000000
Binary files a/chatter/source/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/adapters.cpython-36.pyc b/chatter/source/__pycache__/adapters.cpython-36.pyc
deleted file mode 100644
index b4f47b3..0000000
Binary files a/chatter/source/__pycache__/adapters.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/chatterbot.cpython-36.pyc b/chatter/source/__pycache__/chatterbot.cpython-36.pyc
deleted file mode 100644
index 4b790ba..0000000
Binary files a/chatter/source/__pycache__/chatterbot.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/comparisons.cpython-36.pyc b/chatter/source/__pycache__/comparisons.cpython-36.pyc
deleted file mode 100644
index e1372db..0000000
Binary files a/chatter/source/__pycache__/comparisons.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/constants.cpython-36.pyc b/chatter/source/__pycache__/constants.cpython-36.pyc
deleted file mode 100644
index e1dd4ef..0000000
Binary files a/chatter/source/__pycache__/constants.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/conversation.cpython-36.pyc b/chatter/source/__pycache__/conversation.cpython-36.pyc
deleted file mode 100644
index e9e644a..0000000
Binary files a/chatter/source/__pycache__/conversation.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/preprocessors.cpython-36.pyc b/chatter/source/__pycache__/preprocessors.cpython-36.pyc
deleted file mode 100644
index 954526b..0000000
Binary files a/chatter/source/__pycache__/preprocessors.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/response_selection.cpython-36.pyc b/chatter/source/__pycache__/response_selection.cpython-36.pyc
deleted file mode 100644
index f1c2ecf..0000000
Binary files a/chatter/source/__pycache__/response_selection.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/trainers.cpython-36.pyc b/chatter/source/__pycache__/trainers.cpython-36.pyc
deleted file mode 100644
index 2c3633d..0000000
Binary files a/chatter/source/__pycache__/trainers.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/__pycache__/utils.cpython-36.pyc b/chatter/source/__pycache__/utils.cpython-36.pyc
deleted file mode 100644
index d4d4097..0000000
Binary files a/chatter/source/__pycache__/utils.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/__pycache__/__init__.cpython-36.pyc b/chatter/source/ext/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 502c4b0..0000000
Binary files a/chatter/source/ext/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/django_chatterbot/__init__.py b/chatter/source/ext/django_chatterbot/__init__.py
deleted file mode 100644
index c683f59..0000000
--- a/chatter/source/ext/django_chatterbot/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-default_app_config = (
- 'chatter.source.ext.django_chatterbot.apps.DjangoChatterBotConfig'
-)
diff --git a/chatter/source/ext/django_chatterbot/abstract_models.py b/chatter/source/ext/django_chatterbot/abstract_models.py
deleted file mode 100644
index 4531186..0000000
--- a/chatter/source/ext/django_chatterbot/abstract_models.py
+++ /dev/null
@@ -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 ''
-
- 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
diff --git a/chatter/source/ext/django_chatterbot/admin.py b/chatter/source/ext/django_chatterbot/admin.py
deleted file mode 100644
index 193c264..0000000
--- a/chatter/source/ext/django_chatterbot/admin.py
+++ /dev/null
@@ -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)
diff --git a/chatter/source/ext/django_chatterbot/apps.py b/chatter/source/ext/django_chatterbot/apps.py
deleted file mode 100644
index b873e3e..0000000
--- a/chatter/source/ext/django_chatterbot/apps.py
+++ /dev/null
@@ -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'
diff --git a/chatter/source/ext/django_chatterbot/factories.py b/chatter/source/ext/django_chatterbot/factories.py
deleted file mode 100644
index 7367b58..0000000
--- a/chatter/source/ext/django_chatterbot/factories.py
+++ /dev/null
@@ -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
diff --git a/chatter/source/ext/django_chatterbot/management/commands/__init__.py b/chatter/source/ext/django_chatterbot/management/commands/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/chatter/source/ext/django_chatterbot/management/commands/train.py b/chatter/source/ext/django_chatterbot/management/commands/train.py
deleted file mode 100644
index d4810b8..0000000
--- a/chatter/source/ext/django_chatterbot/management/commands/train.py
+++ /dev/null
@@ -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))
diff --git a/chatter/source/ext/django_chatterbot/migrations/0001_initial.py b/chatter/source/ext/django_chatterbot/migrations/0001_initial.py
deleted file mode 100644
index 9c20907..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0001_initial.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0002_statement_extra_data.py b/chatter/source/ext/django_chatterbot/migrations/0002_statement_extra_data.py
deleted file mode 100644
index 5ed2f4a..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0002_statement_extra_data.py
+++ /dev/null
@@ -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,
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0003_change_occurrence_default.py b/chatter/source/ext/django_chatterbot/migrations/0003_change_occurrence_default.py
deleted file mode 100644
index 8da6869..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0003_change_occurrence_default.py
+++ /dev/null
@@ -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),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0004_rename_in_response_to.py b/chatter/source/ext/django_chatterbot/migrations/0004_rename_in_response_to.py
deleted file mode 100644
index 7860d49..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0004_rename_in_response_to.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0005_statement_created_at.py b/chatter/source/ext/django_chatterbot/migrations/0005_statement_created_at.py
deleted file mode 100644
index 7b38f00..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0005_statement_created_at.py
+++ /dev/null
@@ -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.'
- ),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0006_create_conversation.py b/chatter/source/ext/django_chatterbot/migrations/0006_create_conversation.py
deleted file mode 100644
index 1cf95d9..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0006_create_conversation.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0007_response_created_at.py b/chatter/source/ext/django_chatterbot/migrations/0007_response_created_at.py
deleted file mode 100644
index 1a0b5ac..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0007_response_created_at.py
+++ /dev/null
@@ -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.'
- ),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0008_update_conversations.py b/chatter/source/ext/django_chatterbot/migrations/0008_update_conversations.py
deleted file mode 100644
index f3bd720..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0008_update_conversations.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0009_tags.py b/chatter/source/ext/django_chatterbot/migrations/0009_tags.py
deleted file mode 100644
index ee71713..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0009_tags.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0010_statement_text.py b/chatter/source/ext/django_chatterbot/migrations/0010_statement_text.py
deleted file mode 100644
index 84940a7..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0010_statement_text.py
+++ /dev/null
@@ -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),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/0011_blank_extra_data.py b/chatter/source/ext/django_chatterbot/migrations/0011_blank_extra_data.py
deleted file mode 100644
index 4f7b327..0000000
--- a/chatter/source/ext/django_chatterbot/migrations/0011_blank_extra_data.py
+++ /dev/null
@@ -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),
- ),
- ]
diff --git a/chatter/source/ext/django_chatterbot/migrations/__init__.py b/chatter/source/ext/django_chatterbot/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/chatter/source/ext/django_chatterbot/models.py b/chatter/source/ext/django_chatterbot/models.py
deleted file mode 100644
index ac51c06..0000000
--- a/chatter/source/ext/django_chatterbot/models.py
+++ /dev/null
@@ -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
diff --git a/chatter/source/ext/django_chatterbot/settings.py b/chatter/source/ext/django_chatterbot/settings.py
deleted file mode 100644
index 802b77d..0000000
--- a/chatter/source/ext/django_chatterbot/settings.py
+++ /dev/null
@@ -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)
diff --git a/chatter/source/ext/django_chatterbot/urls.py b/chatter/source/ext/django_chatterbot/urls.py
deleted file mode 100644
index 079005d..0000000
--- a/chatter/source/ext/django_chatterbot/urls.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.conf.urls import url
-from .views import ChatterBotView
-
-
-urlpatterns = [
- url(
- r'^$',
- ChatterBotView.as_view(),
- name='chatterbot',
- ),
-]
diff --git a/chatter/source/ext/django_chatterbot/views.py b/chatter/source/ext/django_chatterbot/views.py
deleted file mode 100644
index d73408e..0000000
--- a/chatter/source/ext/django_chatterbot/views.py
+++ /dev/null
@@ -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)
diff --git a/chatter/source/ext/sqlalchemy_app/__init__.py b/chatter/source/ext/sqlalchemy_app/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/chatter/source/ext/sqlalchemy_app/__pycache__/__init__.cpython-36.pyc b/chatter/source/ext/sqlalchemy_app/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 2faf4fc..0000000
Binary files a/chatter/source/ext/sqlalchemy_app/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/sqlalchemy_app/__pycache__/models.cpython-36.pyc b/chatter/source/ext/sqlalchemy_app/__pycache__/models.cpython-36.pyc
deleted file mode 100644
index feb27f8..0000000
Binary files a/chatter/source/ext/sqlalchemy_app/__pycache__/models.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/ext/sqlalchemy_app/__pycache__/types.cpython-36.pyc b/chatter/source/ext/sqlalchemy_app/__pycache__/types.cpython-36.pyc
deleted file mode 100644
index 93e7532..0000000
Binary files a/chatter/source/ext/sqlalchemy_app/__pycache__/types.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/__init__.cpython-36.pyc b/chatter/source/input/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 360dbeb..0000000
Binary files a/chatter/source/input/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/gitter.cpython-36.pyc b/chatter/source/input/__pycache__/gitter.cpython-36.pyc
deleted file mode 100644
index 15765b3..0000000
Binary files a/chatter/source/input/__pycache__/gitter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/hipchat.cpython-36.pyc b/chatter/source/input/__pycache__/hipchat.cpython-36.pyc
deleted file mode 100644
index 9ce7312..0000000
Binary files a/chatter/source/input/__pycache__/hipchat.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/input_adapter.cpython-36.pyc b/chatter/source/input/__pycache__/input_adapter.cpython-36.pyc
deleted file mode 100644
index 59defa7..0000000
Binary files a/chatter/source/input/__pycache__/input_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/mailgun.cpython-36.pyc b/chatter/source/input/__pycache__/mailgun.cpython-36.pyc
deleted file mode 100644
index 6ca78af..0000000
Binary files a/chatter/source/input/__pycache__/mailgun.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/microsoft.cpython-36.pyc b/chatter/source/input/__pycache__/microsoft.cpython-36.pyc
deleted file mode 100644
index 58f5b4e..0000000
Binary files a/chatter/source/input/__pycache__/microsoft.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/terminal.cpython-36.pyc b/chatter/source/input/__pycache__/terminal.cpython-36.pyc
deleted file mode 100644
index ed2c5bd..0000000
Binary files a/chatter/source/input/__pycache__/terminal.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/input/__pycache__/variable_input_type_adapter.cpython-36.pyc b/chatter/source/input/__pycache__/variable_input_type_adapter.cpython-36.pyc
deleted file mode 100644
index 593d964..0000000
Binary files a/chatter/source/input/__pycache__/variable_input_type_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/__init__.cpython-36.pyc b/chatter/source/logic/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 56ea4ef..0000000
Binary files a/chatter/source/logic/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/best_match.cpython-36.pyc b/chatter/source/logic/__pycache__/best_match.cpython-36.pyc
deleted file mode 100644
index da71469..0000000
Binary files a/chatter/source/logic/__pycache__/best_match.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/logic_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/logic_adapter.cpython-36.pyc
deleted file mode 100644
index ca2c83d..0000000
Binary files a/chatter/source/logic/__pycache__/logic_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/low_confidence.cpython-36.pyc b/chatter/source/logic/__pycache__/low_confidence.cpython-36.pyc
deleted file mode 100644
index a156f97..0000000
Binary files a/chatter/source/logic/__pycache__/low_confidence.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/mathematical_evaluation.cpython-36.pyc b/chatter/source/logic/__pycache__/mathematical_evaluation.cpython-36.pyc
deleted file mode 100644
index 452d69f..0000000
Binary files a/chatter/source/logic/__pycache__/mathematical_evaluation.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/multi_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/multi_adapter.cpython-36.pyc
deleted file mode 100644
index e3517c8..0000000
Binary files a/chatter/source/logic/__pycache__/multi_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/no_knowledge_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/no_knowledge_adapter.cpython-36.pyc
deleted file mode 100644
index 7d286d6..0000000
Binary files a/chatter/source/logic/__pycache__/no_knowledge_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/specific_response.cpython-36.pyc b/chatter/source/logic/__pycache__/specific_response.cpython-36.pyc
deleted file mode 100644
index 179e73d..0000000
Binary files a/chatter/source/logic/__pycache__/specific_response.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/logic/__pycache__/time_adapter.cpython-36.pyc b/chatter/source/logic/__pycache__/time_adapter.cpython-36.pyc
deleted file mode 100644
index 9cbf60e..0000000
Binary files a/chatter/source/logic/__pycache__/time_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/__init__.cpython-36.pyc b/chatter/source/output/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 0f996ed..0000000
Binary files a/chatter/source/output/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/gitter.cpython-36.pyc b/chatter/source/output/__pycache__/gitter.cpython-36.pyc
deleted file mode 100644
index 37a8524..0000000
Binary files a/chatter/source/output/__pycache__/gitter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/hipchat.cpython-36.pyc b/chatter/source/output/__pycache__/hipchat.cpython-36.pyc
deleted file mode 100644
index 7d2dec4..0000000
Binary files a/chatter/source/output/__pycache__/hipchat.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/mailgun.cpython-36.pyc b/chatter/source/output/__pycache__/mailgun.cpython-36.pyc
deleted file mode 100644
index 3295a1a..0000000
Binary files a/chatter/source/output/__pycache__/mailgun.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/microsoft.cpython-36.pyc b/chatter/source/output/__pycache__/microsoft.cpython-36.pyc
deleted file mode 100644
index 73bc7a2..0000000
Binary files a/chatter/source/output/__pycache__/microsoft.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/output_adapter.cpython-36.pyc b/chatter/source/output/__pycache__/output_adapter.cpython-36.pyc
deleted file mode 100644
index 478d6c1..0000000
Binary files a/chatter/source/output/__pycache__/output_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/output/__pycache__/terminal.cpython-36.pyc b/chatter/source/output/__pycache__/terminal.cpython-36.pyc
deleted file mode 100644
index 98a931b..0000000
Binary files a/chatter/source/output/__pycache__/terminal.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/__init__.cpython-36.pyc b/chatter/source/storage/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index 2eb08ba..0000000
Binary files a/chatter/source/storage/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/django_storage.cpython-36.pyc b/chatter/source/storage/__pycache__/django_storage.cpython-36.pyc
deleted file mode 100644
index c8e1f3a..0000000
Binary files a/chatter/source/storage/__pycache__/django_storage.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/mongodb.cpython-36.pyc b/chatter/source/storage/__pycache__/mongodb.cpython-36.pyc
deleted file mode 100644
index 4ef2bb1..0000000
Binary files a/chatter/source/storage/__pycache__/mongodb.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/sql_storage.cpython-36.pyc b/chatter/source/storage/__pycache__/sql_storage.cpython-36.pyc
deleted file mode 100644
index b8acba2..0000000
Binary files a/chatter/source/storage/__pycache__/sql_storage.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/__pycache__/storage_adapter.cpython-36.pyc b/chatter/source/storage/__pycache__/storage_adapter.cpython-36.pyc
deleted file mode 100644
index b3b34df..0000000
Binary files a/chatter/source/storage/__pycache__/storage_adapter.cpython-36.pyc and /dev/null differ
diff --git a/chatter/source/storage/django_storage.py b/chatter/source/storage/django_storage.py
deleted file mode 100644
index 5642b2c..0000000
--- a/chatter/source/storage/django_storage.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from . import StorageAdapter
-from .. import constants
-
-
-class DjangoStorageAdapter(StorageAdapter):
- """
- Storage adapter that allows ChatterBot to interact with
- Django storage backends.
- """
-
- def __init__(self, **kwargs):
- super(DjangoStorageAdapter, self).__init__(**kwargs)
-
- self.adapter_supports_queries = False
- self.django_app_name = kwargs.get(
- 'django_app_name',
- constants.DEFAULT_DJANGO_APP_NAME
- )
-
- def get_statement_model(self):
- from django.apps import apps
- return apps.get_model(self.django_app_name, 'Statement')
-
- def get_response_model(self):
- from django.apps import apps
- return apps.get_model(self.django_app_name, 'Response')
-
- def get_conversation_model(self):
- from django.apps import apps
- return apps.get_model(self.django_app_name, 'Conversation')
-
- def get_tag_model(self):
- from django.apps import apps
- return apps.get_model(self.django_app_name, 'Tag')
-
- def count(self):
- Statement = self.get_model('statement')
- return Statement.objects.count()
-
- def find(self, statement_text):
- Statement = self.get_model('statement')
- try:
- return Statement.objects.get(text=statement_text)
- except Statement.DoesNotExist as e:
- self.logger.info(str(e))
- return None
-
- def filter(self, **kwargs):
- """
- Returns a list of statements in the database
- that match the parameters specified.
- """
- from django.db.models import Q
- Statement = self.get_model('statement')
-
- order = kwargs.pop('order_by', None)
-
- RESPONSE_CONTAINS = 'in_response_to__contains'
-
- if RESPONSE_CONTAINS in kwargs:
- value = kwargs[RESPONSE_CONTAINS]
- del kwargs[RESPONSE_CONTAINS]
- kwargs['in_response__response__text'] = value
-
- kwargs_copy = kwargs.copy()
-
- for kwarg in kwargs_copy:
- value = kwargs[kwarg]
- del kwargs[kwarg]
- kwarg = kwarg.replace('in_response_to', 'in_response')
- kwargs[kwarg] = value
-
- if 'in_response' in kwargs:
- responses = kwargs['in_response']
- del kwargs['in_response']
-
- if responses:
- kwargs['in_response__response__text__in'] = []
- for response in responses:
- kwargs['in_response__response__text__in'].append(response)
- else:
- kwargs['in_response'] = None
-
- parameters = {}
- if 'in_response__response__text' in kwargs:
- value = kwargs['in_response__response__text']
- parameters['responses__statement__text'] = value
-
- statements = Statement.objects.filter(Q(**kwargs) | Q(**parameters))
-
- if order:
- statements = statements.order_by(order)
-
- return statements
-
- def update(self, statement):
- """
- Update the provided statement.
- """
- Statement = self.get_model('statement')
- Response = self.get_model('response')
-
- response_statement_cache = statement.response_statement_cache
-
- statement, created = Statement.objects.get_or_create(text=statement.text)
- statement.extra_data = getattr(statement, 'extra_data', '')
- statement.save()
-
- for _response_statement in response_statement_cache:
-
- response_statement, created = Statement.objects.get_or_create(
- text=_response_statement.text
- )
- response_statement.extra_data = getattr(_response_statement, 'extra_data', '')
- response_statement.save()
-
- Response.objects.create(
- statement=response_statement,
- response=statement
- )
-
- return statement
-
- def get_random(self):
- """
- Returns a random statement from the database
- """
- Statement = self.get_model('statement')
- return Statement.objects.order_by('?').first()
-
- def remove(self, statement_text):
- """
- Removes the statement that matches the input text.
- Removes any responses from statements if the response text matches the
- input text.
- """
- from django.db.models import Q
-
- Statement = self.get_model('statement')
- Response = self.get_model('response')
-
- statements = Statement.objects.filter(text=statement_text)
-
- responses = Response.objects.filter(
- Q(statement__text=statement_text) | Q(response__text=statement_text)
- )
-
- responses.delete()
- statements.delete()
-
- def get_latest_response(self, conversation_id):
- """
- Returns the latest response in a conversation if it exists.
- Returns None if a matching conversation cannot be found.
- """
- Response = self.get_model('response')
-
- response = Response.objects.filter(
- conversations__id=conversation_id
- ).order_by(
- 'created_at'
- ).last()
-
- if not response:
- return None
-
- return response.response
-
- def create_conversation(self):
- """
- Create a new conversation.
- """
- Conversation = self.get_model('conversation')
- conversation = Conversation.objects.create()
- return conversation.id
-
- def add_to_conversation(self, conversation_id, statement, response):
- """
- Add the statement and response to the conversation.
- """
- Statement = self.get_model('statement')
- Response = self.get_model('response')
-
- first_statement, created = Statement.objects.get_or_create(text=statement.text)
- first_response, created = Statement.objects.get_or_create(text=response.text)
-
- response = Response.objects.create(
- statement=first_statement,
- response=first_response
- )
-
- response.conversations.add(conversation_id)
-
- def drop(self):
- """
- Remove all data from the database.
- """
- Statement = self.get_model('statement')
- Response = self.get_model('response')
- Conversation = self.get_model('conversation')
- Tag = self.get_model('tag')
-
- Statement.objects.all().delete()
- Response.objects.all().delete()
- Conversation.objects.all().delete()
- Tag.objects.all().delete()
-
- def get_response_statements(self):
- """
- Return only statements that are in response to another statement.
- A statement must exist which lists the closest matching statement in the
- in_response_to field. Otherwise, the logic adapter may find a closest
- matching statement that does not have a known response.
- """
- Statement = self.get_model('statement')
- Response = self.get_model('response')
-
- responses = Response.objects.all()
-
- return Statement.objects.filter(in_response__in=responses)
diff --git a/coglint/__init__.py b/coglint/__init__.py
new file mode 100644
index 0000000..87f61bb
--- /dev/null
+++ b/coglint/__init__.py
@@ -0,0 +1,5 @@
+from .coglint import CogLint
+
+
+def setup(bot):
+ bot.add_cog(CogLint(bot))
diff --git a/coglint/coglint.py b/coglint/coglint.py
new file mode 100644
index 0000000..608db67
--- /dev/null
+++ b/coglint/coglint.py
@@ -0,0 +1,89 @@
+import discord
+from pylint import epylint as lint
+from redbot.core import Config
+from redbot.core import commands
+from redbot.core.bot import Red
+from redbot.core.data_manager import cog_data_path
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class CogLint(Cog):
+ """
+ Automatically lint code in python codeblocks
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {
+ "lint": True
+ }
+ default_guild = {}
+
+ self.path = str(cog_data_path(self)).replace('\\', '/')
+
+ self.do_lint = None
+ self.counter = 0
+
+ # self.answer_path = self.path + "/tmpfile.py"
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @commands.command()
+ async def autolint(self, ctx: commands.Context):
+ """Toggles automatically linting code"""
+ curr = await self.config.lint()
+
+ self.do_lint = not curr
+ await self.config.lint.set(not curr)
+ await ctx.send("Autolinting is now set to {}".format(not curr))
+
+ @commands.command()
+ async def lint(self, ctx: commands.Context, *, code):
+ """Lint python code
+
+ Toggle autolinting with `[p]autolint`
+ """
+ await self.lint_message(ctx.message)
+ await ctx.send("Hello World")
+
+ async def lint_code(self, code):
+ self.counter += 1
+ path = self.path + "/{}.py".format(self.counter)
+ with open(path, 'w') as codefile:
+ codefile.write(code)
+
+ future = await self.bot.loop.run_in_executor(None, lint.py_run, path, 'return_std=True')
+
+ if future:
+ (pylint_stdout, pylint_stderr) = future
+ else:
+ (pylint_stdout, pylint_stderr) = None, None
+
+ # print(pylint_stderr)
+ # print(pylint_stdout)
+
+ return pylint_stdout, pylint_stderr
+
+ async def lint_message(self, message):
+ if self.do_lint is None:
+ self.do_lint = await self.config.lint()
+ if not self.do_lint:
+ return
+ code_blocks = message.content.split('```')[1::2]
+
+ for c in code_blocks:
+ is_python, code = c.split(None, 1)
+ is_python = is_python.lower() == 'python'
+ if is_python: # Then we're in business
+ linted, errors = await self.lint_code(code)
+ linted = linted.getvalue()
+ errors = errors.getvalue()
+ await message.channel.send(linted)
+ # await message.channel.send(errors)
+
+ async def on_message(self, message: discord.Message):
+ await self.lint_message(message)
diff --git a/coglint/info..json b/coglint/info..json
new file mode 100644
index 0000000..6436b6d
--- /dev/null
+++ b/coglint/info..json
@@ -0,0 +1,20 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Lint python code posted in chat",
+ "hidden": true,
+ "install_msg": "Thank you for installing CogLint! Get started with `[p]load coglint` and `[p]help CogLint`",
+ "requirements": [],
+ "short": "Python cog linter",
+ "tags": [
+ "bobloy",
+ "utils",
+ "tools"
+ ]
+}
\ No newline at end of file
diff --git a/dad/__init__.py b/dad/__init__.py
new file mode 100644
index 0000000..0b94f16
--- /dev/null
+++ b/dad/__init__.py
@@ -0,0 +1,5 @@
+from .dad import Dad
+
+
+def setup(bot):
+ bot.add_cog(Dad(bot))
diff --git a/dad/dad.py b/dad/dad.py
new file mode 100644
index 0000000..06bfb8d
--- /dev/null
+++ b/dad/dad.py
@@ -0,0 +1,112 @@
+from collections import defaultdict
+from datetime import datetime, timedelta
+from typing import Any
+
+import aiohttp
+import discord
+from redbot.core import Config, checks
+from redbot.core import commands
+from redbot.core.bot import Red
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+async def fetch_url(session, url):
+ async with session.get(url) as response:
+ assert response.status == 200
+ return await response.json()
+
+
+class Dad(Cog):
+ """
+ Dad jokes
+
+ Nicknaming user idea comes from https://github.com/Vexs/DadBot
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=6897100, force_registration=True)
+
+ default_guild = {
+ "enabled": False,
+ "nickname": False,
+ "cooldown": 240
+ }
+
+ self.config.register_guild(**default_guild)
+
+ self.cooldown = defaultdict(datetime.now)
+
+ @commands.command()
+ async def dadjoke(self, ctx: commands.Context):
+ headers = {"User-Agent": "FoxV3 (https://github.com/bobloy/Fox-V3)",
+ "Accept": "application/json"}
+
+ async with aiohttp.ClientSession(headers=headers) as session:
+ joke = await fetch_url(session, 'https://icanhazdadjoke.com/')
+
+ em = discord.Embed()
+ em.set_image(url="https://icanhazdadjoke.com/j/{}.png".format(joke['id']))
+
+ await ctx.send(embed=em)
+
+ @commands.group()
+ @checks.admin()
+ async def dad(self, ctx: commands.Context):
+ """Dad joke superhub"""
+ pass
+
+ @dad.command(name='toggle')
+ async def dad_toggle(self, ctx: commands.Context):
+ """Toggle automatic dad jokes on or off"""
+ is_on = await self.config.guild(ctx.guild).enabled()
+ await self.config.guild(ctx.guild).enabled.set(not is_on)
+ await ctx.send("Auto dad jokes are now set to {}".format(not is_on))
+
+ @dad.command(name='nickname')
+ async def dad_nickname(self, ctx: commands.Context):
+ """Toggle nicknaming"""
+ is_on = await self.config.guild(ctx.guild).nickname()
+ await self.config.guild(ctx.guild).nickname.set(not is_on)
+ await ctx.send("Nicknaming is now set to {}".format(not is_on))
+
+ @dad.command(name='cooldown')
+ async def dad_cooldown(self, ctx: commands.Context, cooldown: int):
+ """Set the auto-joke cooldown"""
+
+ await self.config.guild(ctx.guild).cooldown.set(cooldown)
+ await ctx.send("Dad joke cooldown is now set to {}".format(cooldown))
+
+ async def on_message(self, message: discord.Message):
+ guild: discord.Guild = message.guild
+ if guild is None:
+ return
+
+ guild_config = self.config.guild(guild)
+ is_on = await guild_config.enabled()
+ if not is_on:
+ return
+
+ if self.cooldown[guild.id] > datetime.now():
+ return
+
+ lower = message.clean_content.lower()
+ lower_split = lower.split()
+ if len(lower_split)==0:
+ return
+
+ if lower_split[0] == "i'm" and len(lower_split) >= 2:
+ if await guild_config.nickname():
+ try:
+ await message.author.edit(nick=lower[4:])
+ except discord.Forbidden:
+ out = lower[4:]
+ else:
+ out = message.author.mention
+ else:
+ out = lower[4:]
+
+ await message.channel.send("Hi {}, I'm {}!".format(out, guild.me.display_name))
+
+ self.cooldown[guild.id] = datetime.now() + timedelta(seconds=(await guild_config.cooldown()))
diff --git a/dad/info..json b/dad/info..json
new file mode 100644
index 0000000..4b8f44c
--- /dev/null
+++ b/dad/info..json
@@ -0,0 +1,20 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Tell dad jokes and give out bad nicknames",
+ "hidden": true,
+ "install_msg": "Thank you for installing Dad. Get started with `[p]load dad`, then `[p]help Dad`",
+ "requirements": [],
+ "short": "Dad joke bot",
+ "tags": [
+ "bobloy",
+ "utils",
+ "tools"
+ ]
+}
\ No newline at end of file
diff --git a/exclusiverole/__init__.py b/exclusiverole/__init__.py
new file mode 100644
index 0000000..8797845
--- /dev/null
+++ b/exclusiverole/__init__.py
@@ -0,0 +1,5 @@
+from .exclusiverole import ExclusiveRole
+
+
+def setup(bot):
+ bot.add_cog(ExclusiveRole(bot))
diff --git a/exclusiverole/exclusiverole.py b/exclusiverole/exclusiverole.py
new file mode 100644
index 0000000..e476854
--- /dev/null
+++ b/exclusiverole/exclusiverole.py
@@ -0,0 +1,92 @@
+import asyncio
+
+import discord
+from redbot.core import Config, checks, commands
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class ExclusiveRole(Cog):
+ """
+ Custom commands
+ Creates commands used to display text and adjust roles
+ """
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9999114111108101)
+ default_guild = {
+ "role_list": []
+ }
+
+ self.config.register_guild(**default_guild)
+
+ @commands.group(no_pm=True, aliases=["exclusiverole"])
+ async def exclusive(self, ctx):
+ """Base command for managing exclusive roles"""
+
+ if not ctx.invoked_subcommand:
+ pass
+
+ @exclusive.command(name="add")
+ @checks.mod_or_permissions(administrator=True)
+ async def exclusive_add(self, ctx, role: discord.Role):
+ """Adds an exclusive role"""
+ if role.id in (await self.config.guild(ctx.guild).role_list()):
+ await ctx.send("That role is already exclusive")
+ return
+
+ async with self.config.guild(ctx.guild).role_list() as rl:
+ rl.append(role.id)
+
+ await self.check_guild(ctx.guild)
+
+ await ctx.send("Exclusive role added")
+
+ @exclusive.command(name="delete")
+ @checks.mod_or_permissions(administrator=True)
+ async def exclusive_delete(self, ctx, role: discord.Role):
+ """Deletes an exclusive role"""
+ if role.id not in (await self.config.guild(ctx.guild).role_list()):
+ await ctx.send("That role is not exclusive")
+ return
+
+ async with self.config.guild(ctx.guild).role_list() as rl:
+ rl.remove(role.id)
+
+ await ctx.send("Exclusive role removed")
+
+ async def check_guild(self, guild: discord.Guild):
+ role_set = set(await self.config.guild(guild).role_list())
+ for member in guild.members:
+ try:
+ await self.remove_non_exclusive_roles(member, role_set=role_set)
+ except discord.Forbidden:
+ pass
+
+ async def remove_non_exclusive_roles(self, member: discord.Member, role_set=None):
+ if role_set is None:
+ role_set = set(await self.config.guild(member.guild).role_list())
+
+ member_set = set([role.id for role in member.roles])
+ to_remove = (member_set - role_set) - {member.guild.default_role.id}
+
+ if to_remove and member_set & role_set:
+ to_remove = [discord.utils.get(member.guild.roles, id=id) for id in to_remove]
+ await member.remove_roles(*to_remove, reason="Exclusive roles")
+
+ async def on_member_update(self, before: discord.Member, after: discord.Member):
+ if before.roles == after.roles:
+ return
+
+ await asyncio.sleep(1)
+
+ role_set = set(await self.config.guild(after.guild).role_list())
+ member_set = set([role.id for role in after.roles])
+
+ if role_set & member_set:
+ try:
+ await self.remove_non_exclusive_roles(after, role_set=role_set)
+ except discord.Forbidden:
+ pass
diff --git a/exclusiverole/info.json b/exclusiverole/info.json
new file mode 100644
index 0000000..8c90983
--- /dev/null
+++ b/exclusiverole/info.json
@@ -0,0 +1,22 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Assign roles to be exclusive, preventing other roles from being added",
+ "hidden": false,
+ "install_msg": "Thank you for installing ExclusiveRole. Get started with `[p]load exclusiverole` and `[p]help ExclusiveRole`",
+ "requirements": [],
+ "short": "Set roles to be exclusive",
+ "tags": [
+ "fox",
+ "bobloy",
+ "utility",
+ "tools",
+ "roles"
+ ]
+}
\ No newline at end of file
diff --git a/fight/fight.py b/fight/fight.py
index e616dcc..5a3e1f9 100644
--- a/fight/fight.py
+++ b/fight/fight.py
@@ -94,7 +94,7 @@ class Fight:
await ctx.send("Current tournament ID: " + await self._activefight(ctx))
if ctx.invoked_subcommand is None:
- await ctx.send_help()
+ pass
# await ctx.send("I can do stuff!")
@fight.command(name="join")
@@ -186,8 +186,8 @@ class Fight:
async def fadmin(self, ctx):
"""Admin command for managing the current tournament"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
-
+ pass
+
@fadmin.command(name="score")
async def fadmin_score(self, ctx, m_id, score1, score2):
"""Set's the score for matchID and clears disputes"""
@@ -207,8 +207,9 @@ class Fight:
async def fightset(self, ctx):
"""Admin command for starting or managing tournaments"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
-
+ pass
+ # await ctx.send("I can do stuff!")
+
@fightset.command(name="emoji")
async def fightset_emoji(self, ctx):
"""Set the global reaction emojis for reporting matches"""
@@ -504,8 +505,8 @@ class Fight:
async def fightset_guild(self, ctx):
"""Adjust guild wide settings"""
if ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group):
- await ctx.send_help()
-
+ pass
+
@fightset_guild.command(name="selfreport")
async def fightset_guild_selfreport(self, ctx):
"""Toggles the ability to self-report scores for all tournaments"""
@@ -667,11 +668,13 @@ class Fight:
async def _embed_tourney(self, ctx, t_id):
"""Prints a pretty embed of the tournament"""
- await ctx.send("_embed_tourney Todo") # ToDo embed tourney
+ #_placeholder Todo
+ pass
async def _comparescores(self, ctx):
"""Checks user submitted scores for inconsistancies"""
- await ctx.send("_comparescores Todo") # ToDo compare scores
+ # _comparescores Todo
+ pass
async def _parseuser(self, guild: discord.Guild, t_id, userid):
"""Finds user in the tournament"""
@@ -1102,7 +1105,7 @@ class Fight:
# **************** Attempt 2, borrow from Squid*******
- async def on_raw_reaction_add(self, emoji: discord.PartialReactionEmoji,
+ async def on_raw_reaction_add(self, emoji: discord.PartialEmoji,
message_id: int, channel_id: int, user_id: int):
"""
Event handler for long term reaction watching.
diff --git a/fight/info.json b/fight/info.json
index 0465295..1e201c9 100644
--- a/fight/info.json
+++ b/fight/info.json
@@ -8,7 +8,7 @@
0
],
"description": "[Incomplete] Cog to organize tournaments within Discord",
- "hidden": false,
+ "hidden": true,
"install_msg": "Thank you for installing Fight. Run with [p]fight or [p]fightset",
"requirements": [],
"short": "[Incomplete] Cog to organize tournaments",
diff --git a/flag/__init__.py b/flag/__init__.py
new file mode 100644
index 0000000..0184952
--- /dev/null
+++ b/flag/__init__.py
@@ -0,0 +1,5 @@
+from .flag import Flag
+
+
+def setup(bot):
+ bot.add_cog(Flag(bot))
diff --git a/flag/flag.py b/flag/flag.py
new file mode 100644
index 0000000..ed1511d
--- /dev/null
+++ b/flag/flag.py
@@ -0,0 +1,191 @@
+from datetime import date, timedelta
+
+import discord
+from redbot.core import Config, checks, commands
+from redbot.core.bot import Red
+from redbot.core.utils.chat_formatting import pagify
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class Flag(Cog):
+ """
+ Set expiring flags on members
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {
+ "days": 31,
+ "dm": True,
+ "flags": {}
+ }
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @checks.is_owner()
+ @commands.guild_only()
+ @commands.command()
+ async def clearallflag(self, ctx: commands.Context):
+ """Clears all flags for all members in this server"""
+
+ await self.config.guild(ctx.guild).flags.clear()
+ await ctx.send("Done")
+
+ @checks.mod_or_permissions(manage_roles=True)
+ @commands.guild_only()
+ @commands.group()
+ async def flagset(self, ctx: commands.Context):
+ """
+ My custom cog
+
+ Extra information goes here
+ """
+ if ctx.invoked_subcommand is None:
+ pass
+
+ @flagset.command(name="expire")
+ async def flagset_expire(self, ctx: commands.Context, days: int):
+ """
+ Set the number of days for flags to expire after for server
+ """
+ await self.config.guild(ctx.guild).days.set(days)
+ await ctx.send("Number of days for new flags to expire is now {} days".format(days))
+
+ @flagset.command(name="dm")
+ async def flagset_dm(self, ctx: commands.Context):
+ """Toggles DM-ing the flags"""
+
+ dm = await self.config.guild(ctx.guild).dm()
+ await self.config.guild(ctx.guild).dm.set(not dm)
+
+ await ctx.send("DM-ing members when they get a flag is now set to **{}**".format(not dm))
+
+ @staticmethod
+ def _flag_template():
+ return {
+ 'reason': "",
+ 'expireyear': 0,
+ 'expiremonth': 0,
+ 'expireday': 0
+ }
+
+ @commands.guild_only()
+ @checks.mod_or_permissions(manage_roles=True)
+ @commands.command()
+ async def flag(self, ctx: commands.Context, member: discord.Member, *, reason):
+ """Flag a member"""
+ guild = ctx.guild
+ await self._check_flags(guild)
+ # clashroyale = self.bot.get_cog('clashroyale')
+ # if clashroyale is None:
+ # await ctx.send("Requires clashroyale cog installed")
+ # return
+
+ flag = self._flag_template()
+ expiredate = date.today()
+ expiredate += timedelta(days=await self.config.guild(guild).days())
+
+ flag['reason'] = reason
+ flag['expireyear'] = expiredate.year
+ flag['expiremonth'] = expiredate.month
+ flag['expireday'] = expiredate.day
+
+ # flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[])
+ # flags.append(flag)
+ # await self.config.guild(guild).flags.set_raw(str(member.id), value=flags)
+
+ async with self.config.guild(guild).flags() as flags:
+ flags[str(member.id)].append(flag)
+
+ outembed = await self._list_flags(member)
+
+ if outembed:
+ await ctx.send(embed=outembed)
+ if await self.config.guild(guild).dm():
+ await member.send(embed=outembed)
+ else:
+ await ctx.send("This member has no flags.. somehow..")
+
+ @commands.guild_only()
+ @checks.mod_or_permissions(manage_roles=True)
+ @commands.command(aliases=['flagclear'])
+ async def clearflag(self, ctx: commands.Context, member: discord.Member):
+ """Clears flags for a member"""
+ guild = ctx.guild
+ await self._check_flags(guild)
+
+ await self.config.guild(guild).flags.set_raw(str(member.id), value=[])
+
+ await ctx.send("Success!")
+
+ @commands.guild_only()
+ @commands.command(aliases=['flaglist'])
+ async def listflag(self, ctx: commands.Context, member: discord.Member):
+ """Lists flags for a member"""
+ server = ctx.guild
+ await self._check_flags(server)
+
+ outembed = await self._list_flags(member)
+
+ if outembed:
+ await ctx.send(embed=outembed)
+ else:
+ await ctx.send("This member has no flags!")
+
+ @commands.guild_only()
+ @commands.command(aliases=['flagall'])
+ async def allflag(self, ctx: commands.Context):
+ """Lists all flags for the server"""
+ guild = ctx.guild
+ await self._check_flags(guild)
+ out = "All flags for {}\n".format(ctx.guild.name)
+
+ flags = await self.config.guild(guild).flags()
+ flag_d = {}
+ for memberid, flag_data in flags.items():
+ if len(flag_data) > 0:
+ member = guild.get_member(int(memberid))
+ flag_d[member.display_name + member.discriminator] = len(flag_data)
+
+ for display_name, flag_count in sorted(flag_d.items()):
+ out += "{} - **{}** flags".format(display_name, flag_count)
+
+ for page in pagify(out):
+ await ctx.send(page)
+
+ async def _list_flags(self, member: discord.Member):
+ """Returns a pretty embed of flags on a member"""
+ flags = await self.config.guild(member.guild).flags.get_raw(str(member.id), default=[])
+
+ embed = discord.Embed(title="Flags for " + member.display_name,
+ description="User has {} active flags".format(len(flags)), color=0x804040)
+ for flag in flags:
+ embed.add_field(name="Reason: " + flag['reason'],
+ value="Expires on " + str(date(flag['expireyear'], flag['expiremonth'], flag['expireday'])),
+ inline=True)
+
+ embed.set_thumbnail(url=member.avatar_url)
+
+ return embed
+
+ async def _check_flags(self, guild: discord.Guild):
+ """Updates and removes expired flags"""
+ flag_data = await self.config.guild(guild).flags()
+ flag_d = {}
+ for memberid, flags in flag_data.items():
+ # for member in guild.members:
+ # flags = await self.config.guild(guild).flags.get_raw(str(member.id), default=[])
+ x = 0
+ while x < len(flags):
+ flag = flags[x]
+ if date.today() >= date(flag['expireyear'], flag['expiremonth'], flag['expireday']):
+ del flags[x]
+ else:
+ x += 1
+
+ await self.config.guild(guild).flags.set_raw(memberid, value=flags)
diff --git a/flag/info..json b/flag/info..json
new file mode 100644
index 0000000..0883f13
--- /dev/null
+++ b/flag/info..json
@@ -0,0 +1,23 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Add expiring flags on members to track warnings or incidents",
+ "hidden": false,
+ "install_msg": "Thank you for installing Flag! Get started with `[p]load flag` and `[p]help Flag`",
+ "requirements": [],
+ "short": "Add expiring flags to members",
+ "tags": [
+ "bobloy",
+ "warning",
+ "warn",
+ "temp",
+ "tools",
+ "warning"
+ ]
+}
\ No newline at end of file
diff --git a/forcemention/__init__.py b/forcemention/__init__.py
new file mode 100644
index 0000000..a2a8ee7
--- /dev/null
+++ b/forcemention/__init__.py
@@ -0,0 +1,5 @@
+from .forcemention import ForceMention
+
+
+def setup(bot):
+ bot.add_cog(ForceMention(bot))
diff --git a/forcemention/forcemention.py b/forcemention/forcemention.py
new file mode 100644
index 0000000..1d0452c
--- /dev/null
+++ b/forcemention/forcemention.py
@@ -0,0 +1,41 @@
+from discord.utils import get
+
+from redbot.core import Config, checks, commands
+
+from redbot.core.bot import Red
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class ForceMention(Cog):
+ """
+ Mention the unmentionables
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {}
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @checks.admin_or_permissions(manage_roles=True)
+ @commands.command()
+ async def forcemention(self, ctx: commands.Context, role: str, *, message=''):
+ """
+ Mentions that role, regardless if it's unmentionable
+ """
+ role_obj = get(ctx.guild.roles, name=role)
+ if role_obj is None:
+ await ctx.maybe_send_embed("Couldn't find role named {}".format(role))
+ return
+
+ if not role_obj.mentionable:
+ await role_obj.edit(mentionable=True)
+ await ctx.send("{}\n{}".format(role_obj.mention, message))
+ await role_obj.edit(mentionable=False)
+ else:
+ await ctx.send("{}\n{}".format(role_obj.mention, message))
diff --git a/forcemention/info..json b/forcemention/info..json
new file mode 100644
index 0000000..3a7b1e1
--- /dev/null
+++ b/forcemention/info..json
@@ -0,0 +1,19 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Mentions roles that are unmentionable",
+ "hidden": false,
+ "install_msg": "Thank you for installing ForceMention! Get started with `[p]load forcemention`, then `[p]forcemention`",
+ "requirements": [],
+ "short": "Mention unmentionables",
+ "tags": [
+ "bobloy",
+ "utils"
+ ]
+}
\ No newline at end of file
diff --git a/hangman/__init__.py b/hangman/__init__.py
new file mode 100644
index 0000000..88598f8
--- /dev/null
+++ b/hangman/__init__.py
@@ -0,0 +1,9 @@
+from .hangman import Hangman
+from redbot.core import data_manager
+
+
+def setup(bot):
+ n = Hangman(bot)
+ data_manager.load_bundled_data(n, __file__)
+ bot.add_cog(n)
+ bot.add_listener(n.on_react, "on_reaction_add")
diff --git a/hangman/hangman.py b/hangman/hangman.py
index 987b3a2..0ca60ee 100644
--- a/hangman/hangman.py
+++ b/hangman/hangman.py
@@ -1,356 +1,356 @@
-import discord
-import os
-
-from discord.ext import commands
+from collections import defaultdict
from random import randint
-from .utils.dataIO import dataIO
-from .utils import checks
+import discord
+from redbot.core import Config, checks, commands
+from redbot.core.data_manager import cog_data_path
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
-class Hangman:
+class Hangman(Cog):
"""Lets anyone play a game of hangman with custom phrases"""
+ navigate = "🔼🔽"
+ letters = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿"
def __init__(self, bot):
self.bot = bot
- self.path = "data/Fox-Cogs/hangman"
- self.file_path = "data/Fox-Cogs/hangman/hangman.json"
- self.answer_path = "data/hangman/hanganswers.txt"
- self.the_data = dataIO.load_json(self.file_path)
- self.winbool = False
- self.letters = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿"
- self.navigate = "🔼🔽"
- self._updateHanglist()
-
- def _updateHanglist(self):
- self.hanglist = (
- """>
- \_________
- |/
- |
- |
- |
- |
- |
- |\___
- """,
-
- """>
- \_________
- |/ |
- |
- |
- |
- |
- |
- |\___
- H""",
-
- """>
- \_________
- |/ |
- | """+self.the_data["theface"]+"""
- |
- |
- |
- |
- |\___
- HA""",
-
- """>
- \________
- |/ |
- | """+self.the_data["theface"]+"""
- | |
- | |
- |
- |
- |\___
- HAN""",
-
-
- """>
- \_________
- |/ |
- | """+self.the_data["theface"]+"""
- | /|
- | |
- |
- |
- |\___
- HANG""",
-
-
- """>
- \_________
- |/ |
- | """+self.the_data["theface"]+"""
- | /|\
- | |
- |
- |
- |\___
- HANGM""",
-
-
-
- """>
- \________
- |/ |
- | """+self.the_data["theface"]+"""
- | /|\
- | |
- | /
- |
- |\___
- HANGMA""",
-
-
- """>
- \________
- |/ |
- | """+self.the_data["theface"]+"""
- | /|\
- | |
- | / \
- |
- |\___
- HANGMAN""")
-
- def save_data(self):
- """Saves the json"""
- dataIO.save_json(self.file_path, self.the_data)
-
+ self.config = Config.get_conf(self, identifier=1049711010310997110)
+ default_guild = {
+ "theface": ':thinking:',
+ "emojis": True,
+ }
+
+ self.config.register_guild(**default_guild)
+
+ self.the_data = defaultdict(
+ lambda: {"running": False, "hangman": 0, "guesses": [], "trackmessage": False, "answer": ''})
+ self.path = str(cog_data_path(self)).replace('\\', '/')
+
+ self.answer_path = self.path + "/bundled_data/hanganswers.txt"
+
+ self.winbool = defaultdict(lambda: False)
+
+ self.hanglist = {}
+
+ async def _update_hanglist(self):
+ for guild in self.bot.guilds:
+ theface = await self.config.guild(guild).theface()
+ self.hanglist[guild] = (
+ """>
+ \_________
+ |/
+ |
+ |
+ |
+ |
+ |
+ |\___
+ """,
+
+ """>
+ \_________
+ |/ |
+ |
+ |
+ |
+ |
+ |
+ |\___
+ H""",
+
+ """>
+ \_________
+ |/ |
+ | """ + theface + """
+ |
+ |
+ |
+ |
+ |\___
+ HA""",
+
+ """>
+ \________
+ |/ |
+ | """ + theface + """
+ | |
+ | |
+ |
+ |
+ |\___
+ HAN""",
+
+ """>
+ \_________
+ |/ |
+ | """ + theface + """
+ | /|
+ | |
+ |
+ |
+ |\___
+ HANG""",
+
+ """>
+ \_________
+ |/ |
+ | """ + theface + """
+ | /|\
+ | |
+ |
+ |
+ |\___
+ HANGM""",
+
+ """>
+ \________
+ |/ |
+ | """ + theface + """
+ | /|\
+ | |
+ | /
+ |
+ |\___
+ HANGMA""",
+
+ """>
+ \________
+ |/ |
+ | """ + theface + """
+ | /|\
+ | |
+ | / \
+ |
+ |\___
+ HANGMAN""")
+
@commands.group(aliases=['sethang'], pass_context=True)
@checks.mod_or_permissions(administrator=True)
async def hangset(self, ctx):
"""Adjust hangman settings"""
if ctx.invoked_subcommand is None:
- await self.bot.send_cmd_help(ctx)
-
+ pass
+
@hangset.command(pass_context=True)
- async def face(self, ctx, theface):
+ async def face(self, ctx: commands.Context, theface):
+ """Set the face of the hangman"""
message = ctx.message
- #Borrowing FlapJack's emoji validation (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
+ # Borrowing FlapJack's emoji validation
+ # (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
if theface[:2] == "<:":
- theface = [r for server in self.bot.servers for r in server.emojis if r.id == theface.split(':')[2][:-1]][0]
-
+ theface = [r for r in self.bot.emojis if r.id == theface.split(':')[2][:-1]][0]
+
try:
# Use the face as reaction to see if it's valid (THANKS FLAPJACK <3)
- await self.bot.add_reaction(message, theface)
- self.the_data["theface"] = str(theface)
- self.save_data()
- self._updateHanglist()
- await self.bot.say("Face has been updated!")
-
+ await message.add_reaction(theface)
except discord.errors.HTTPException:
- await self.bot.say("That's not an emoji I recognize.")
-
+ await ctx.send("That's not an emoji I recognize.")
+ return
+
+ await self.config.guild(ctx.guild).theface.set(theface)
+ await self._update_hanglist()
+ await ctx.send("Face has been updated!")
+
+ @hangset.command(pass_context=True)
+ async def toggleemoji(self, ctx: commands.Context):
+ """Toggles whether to automatically react with the alphabet"""
+
+ current = await self.config.guild(ctx.guild).emojis()
+ await self.config.guild(ctx.guild).emojis.set(not current)
+ await ctx.send("Emoji Letter reactions have been set to {}".format(not current))
+
@commands.command(aliases=['hang'], pass_context=True)
- async def hangman(self, ctx, guess: str=None):
+ async def hangman(self, ctx, guess: str = None):
"""Play a game of hangman against the bot!"""
if guess is None:
- if self.the_data["running"]:
- await self.bot.say("Game of hangman is already running!\nEnter your guess!")
- self._printgame()
+ if self.the_data[ctx.guild]["running"]:
+ await ctx.send("Game of hangman is already running!\nEnter your guess!")
+ await self._printgame(ctx.channel)
"""await self.bot.send_cmd_help(ctx)"""
else:
- await self.bot.say("Starting a game of hangman!")
- self._startgame()
- await self._printgame()
- elif not self.the_data["running"]:
- await self.bot.say("Game of hangman is not yet running!\nStarting a game of hangman!")
- self._startgame()
- await self._printgame()
- else:
- await self._guessletter(guess)
-
-
-
- def _startgame(self):
+ await ctx.send("Starting a game of hangman!")
+ self._startgame(ctx.guild)
+ await self._printgame(ctx.channel)
+ elif not self.the_data[ctx.guild]["running"]:
+ await ctx.send("Game of hangman is not yet running!\nStarting a game of hangman!")
+ self._startgame(ctx.guild)
+ await self._printgame(ctx.channel)
+ else:
+ await ctx.send("Guess by reacting to the message")
+ # await self._guessletter(guess, ctx.channel)
+
+ def _startgame(self, guild):
"""Starts a new game of hangman"""
- self.the_data["answer"] = self._getphrase().upper()
- self.the_data["hangman"] = 0
- self.the_data["guesses"] = []
- self.winbool = False
- self.the_data["running"] = True
- self.the_data["trackmessage"] = False
- self.save_data()
-
- def _stopgame(self):
+ self.the_data[guild]["answer"] = self._getphrase().upper()
+ self.the_data[guild]["hangman"] = 0
+ self.the_data[guild]["guesses"] = []
+ self.winbool[guild] = False
+ self.the_data[guild]["running"] = True
+ self.the_data[guild]["trackmessage"] = False
+
+ def _stopgame(self, guild):
"""Stops the game in current state"""
- self.the_data["running"] = False
- self.save_data()
-
- async def _checkdone(self, channel=None):
- if self.winbool:
- if channel:
- await self.bot.send_message(channel, "You Win!")
- else:
- await self.bot.say("You Win!")
- self._stopgame()
-
- if self.the_data["hangman"] >= 7:
- if channel:
- await self.bot.send_message(channel, "You Lose!\nThe Answer was: **"+self.the_data["answer"]+"**")
- else:
- await self.bot.say("You Lose!\nThe Answer was: **"+self.the_data["answer"]+"**")
-
- self._stopgame()
-
+ self.the_data[guild]["running"] = False
+ self.the_data[guild]["trackmessage"] = False
+
+ async def _checkdone(self, channel):
+ if self.winbool[channel.guild]:
+ await channel.send("You Win!")
+ self._stopgame(channel.guild)
+ elif self.the_data[channel.guild]["hangman"] >= 7:
+ await channel.send("You Lose!\nThe Answer was: **" + self.the_data[channel.guild]["answer"] + "**")
+
+ self._stopgame(channel.guild)
+
def _getphrase(self):
"""Get a new phrase for the game and returns it"""
- phrasefile = open(self.answer_path, 'r')
- phrases = phrasefile.readlines()
-
+
+ with open(self.answer_path, 'r') as phrasefile:
+ phrases = phrasefile.readlines()
+
outphrase = ""
while outphrase == "":
- outphrase = phrases[randint(0, len(phrases)-1)].partition(" (")[0]
-# outphrase = phrases[randint(0,10)].partition(" (")[0]
+ outphrase = phrases[randint(0, len(phrases) - 1)].partition(" (")[0]
return outphrase
-
- def _hideanswer(self):
+
+ def _hideanswer(self, guild):
"""Returns the obscured answer"""
out_str = ""
-
- self.winbool = True
- for i in self.the_data["answer"]:
+
+ self.winbool[guild] = True
+ for i in self.the_data[guild]["answer"]:
if i == " " or i == "-":
- out_str += i*2
- elif i in self.the_data["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
- out_str += "__"+i+"__ "
+ out_str += i * 2
+ elif i in self.the_data[guild]["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
+ out_str += "__" + i + "__ "
else:
out_str += "**\_** "
- self.winbool = False
-
+ self.winbool[guild] = False
+
return out_str
-
- def _guesslist(self):
+
+ def _guesslist(self, guild):
"""Returns the current letter list"""
out_str = ""
- for i in self.the_data["guesses"]:
+ for i in self.the_data[guild]["guesses"]:
out_str += str(i) + ","
out_str = out_str[:-1]
-
+
return out_str
-
- async def _guessletter(self, guess, channel=None):
+
+ async def _guessletter(self, guess, message):
"""Checks the guess on a letter and prints game if acceptable guess"""
- if not guess.upper() in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or not len(guess) == 1:
- if channel:
- await self.bot.send_message(channel, "Invalid guess. Only A-Z is accepted")
- else:
- await self.bot.say("Invalid guess. Only A-Z is accepted")
+ channel = message.channel
+ if guess.upper() not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or len(guess) != 1:
+ await channel.send("Invalid guess. Only A-Z is accepted")
return
- if guess.upper() in self.the_data["guesses"]:
- if channel:
- await self.bot.send_message(channel, "Already guessed that! Try again")
- else:
- await self.bot.say("Already guessed that! Try again")
+ if guess.upper() in self.the_data[channel.guild]["guesses"]:
+ await channel.send("Already guessed that! Try again")
return
+ if guess.upper() not in self.the_data[channel.guild]["answer"]:
+ self.the_data[channel.guild]["hangman"] += 1
- if not guess.upper() in self.the_data["answer"]:
- self.the_data["hangman"] += 1
-
- self.the_data["guesses"].append(guess.upper())
- self.save_data()
-
- await self._printgame(channel)
-
- async def _on_react(self, reaction, user):
+ self.the_data[channel.guild]["guesses"].append(guess.upper())
+
+ await self._reprintgame(message)
+
+ async def on_react(self, reaction, user):
""" Thanks to flapjack reactpoll for guidelines
https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""
-
-
-
- if not self.the_data["trackmessage"]:
+
+ if reaction.message.id != self.the_data[user.guild]["trackmessage"]:
return
-
+
if user == self.bot.user:
- return # Don't remove bot's own reactions
+ return # Don't react to bot's own reactions
message = reaction.message
emoji = reaction.emoji
-
- if not message.id == self.the_data["trackmessage"]:
- return
-
+
if str(emoji) in self.letters:
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[self.letters.index(str(emoji))]
- await self._guessletter(letter, message.channel)
-
-
+ await self._guessletter(letter, message)
+ await message.remove_reaction(emoji, user)
+ await message.remove_reaction(emoji, self.bot.user)
+
if str(emoji) in self.navigate:
if str(emoji) == self.navigate[0]:
await self._reactmessage_am(message)
-
+
if str(emoji) == self.navigate[-1]:
await self._reactmessage_nz(message)
-
-
+
+ async def _try_clear_reactions(self, message):
+ try:
+ await message.clear_reactions()
+ except discord.Forbidden:
+ pass
+
async def _reactmessage_menu(self, message):
"""React with menu options"""
- await self.bot.clear_reactions(message)
-
- await self.bot.add_reaction(message, self.navigate[0])
- await self.bot.add_reaction(message, self.navigate[-1])
-
+ if not await self.config.guild(message.guild).emojis():
+ return
+
+ await self._try_clear_reactions(message)
+
+ await message.add_reaction(self.navigate[0])
+ await message.add_reaction(self.navigate[-1])
+
async def _reactmessage_am(self, message):
- await self.bot.clear_reactions(message)
+ if not await self.config.guild(message.guild).emojis():
+ return
+
+ await self._try_clear_reactions(message)
for x in range(len(self.letters)):
- if x in [i for i,b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist()]:
- await self.bot.add_reaction(message, self.letters[x])
-
- await self.bot.add_reaction(message, self.navigate[-1])
+ if x in [i for i, b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist(message.guild)]:
+ await message.add_reaction(self.letters[x])
+ await message.add_reaction(self.navigate[-1])
async def _reactmessage_nz(self, message):
- await self.bot.clear_reactions(message)
+ if not await self.config.guild(message.guild).emojis():
+ return
+
+ await self._try_clear_reactions(message)
for x in range(len(self.letters)):
- if x in [i for i,b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist()]:
- await self.bot.add_reaction(message, self.letters[x+13])
-
- await self.bot.add_reaction(message, self.navigate[0])
+ if x in [i for i, b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist(message.guild)]:
+ await message.add_reaction(self.letters[x + 13])
+
+ await message.add_reaction(self.navigate[0])
+
+ def _make_say(self, guild):
+ c_say = "Guess this: " + str(self._hideanswer(guild)) + "\n"
+ c_say += "Used Letters: " + str(self._guesslist(guild)) + "\n"
+ c_say += self.hanglist[guild][self.the_data[guild]["hangman"]] + "\n"
+ c_say += self.navigate[0] + " for A-M, " + self.navigate[-1] + " for N-Z"
+
+ return c_say
+
+ async def _reprintgame(self, message):
+ if message.guild not in self.hanglist:
+ await self._update_hanglist()
+ c_say = self._make_say(message.guild)
- async def _printgame(self, channel=None):
+ await message.edit(content=c_say)
+ self.the_data[message.guild]["trackmessage"] = message.id
+
+ await self._checkdone(message.channel)
+
+ async def _printgame(self, channel):
"""Print the current state of game"""
- cSay = ("Guess this: " + str(self._hideanswer()) + "\n"
- + "Used Letters: " + str(self._guesslist()) + "\n"
- + self.hanglist[self.the_data["hangman"]] + "\n"
- + self.navigate[0]+" for A-M, "+self.navigate[-1]+" for N-Z")
- if channel:
- message = await self.bot.send_message(channel, cSay)
- else:
- message = await self.bot.say(cSay)
-
- self.the_data["trackmessage"] = message.id
- self.save_data()
+ if channel.guild not in self.hanglist:
+ await self._update_hanglist()
+
+ c_say = self._make_say(channel.guild)
+
+ message = await channel.send(c_say)
+
+ self.the_data[channel.guild]["trackmessage"] = message.id
+
await self._reactmessage_menu(message)
await self._checkdone(channel)
-
-
-def check_folders():
- if not os.path.exists("data/Fox-Cogs"):
- print("Creating data/Fox-Cogs folder...")
- os.makedirs("data/Fox-Cogs")
-
- if not os.path.exists("data/Fox-Cogs/hangman"):
- print("Creating data/Fox-Cogs/hangman folder...")
- os.makedirs("data/Fox-Cogs/hangman")
-
-
-def check_files():
- if not dataIO.is_valid_json("data/Fox-Cogs/hangman/hangman.json"):
- dataIO.save_json("data/Fox-Cogs/hangman/hangman.json", {"running": False, "hangman": 0, "guesses": [], "theface": "<:never:336861463446814720>", "trackmessage": False})
-
-
-def setup(bot):
- check_folders()
- check_files()
- n = Hangman(bot)
- bot.add_cog(n)
- bot.add_listener(n._on_react, "on_reaction_add")
-
diff --git a/hangman/info.json b/hangman/info.json
index ac146f1..c9dadf0 100644
--- a/hangman/info.json
+++ b/hangman/info.json
@@ -1,14 +1,20 @@
{
- "AUTHOR": "Bobloy",
- "DESCRIPTION": "Hangman Cog for Red Discord bot. Play a game of Hangman with your friends!",
- "INSTALL_MSG": "Thank you for installing Hangman! Play with [p]hangman, edit with [p]hangset",
- "NAME": "Hangman",
- "SHORT": "Play a game of Hangman with your friends!",
- "TAGS": [
- "fox",
- "bobloy",
- "fun",
- "game"
- ],
- "HIDDEN": false
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Play Hangman with your friends",
+ "hidden": false,
+ "install_msg": "Thank you for installing Hangman! Get started with `[p]load hangman`, then `[p]help Hangman`",
+ "requirements": [],
+ "short": "Play Hangman",
+ "tags": [
+ "game",
+ "fun",
+ "bobloy"
+ ]
}
\ No newline at end of file
diff --git a/howdoi/howdoi.py b/howdoi/howdoi.py
index eee3f35..17fc623 100644
--- a/howdoi/howdoi.py
+++ b/howdoi/howdoi.py
@@ -1,6 +1,6 @@
import discord
-from discord.ext import commands
+
from .utils.chat_formatting import pagify
from .utils.chat_formatting import box
@@ -33,7 +33,7 @@ class Howdoi:
"""Adjust howdoi settings
Settings are reset on reload"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
+ pass
@howdoiset.command(pass_context=True, name="answers")
async def howdoiset_answers(self, ctx, num_answers: int=1):
diff --git a/immortal/immortal.py b/immortal/immortal.py
deleted file mode 100644
index c20908c..0000000
--- a/immortal/immortal.py
+++ /dev/null
@@ -1,243 +0,0 @@
-import discord
-import asyncio
-import os
-from datetime import datetime
-from discord.ext import commands
-
-from .utils.dataIO import dataIO
-from .utils import checks
-
-
-class Immortal:
- """Creates a goodbye message when people leave"""
-
- def __init__(self, bot):
- self.bot = bot
- self.path = "data/Fox-Cogs/immortal"
- self.file_path = "data/Fox-Cogs/immortal/immortal.json"
- self.the_data = dataIO.load_json(self.file_path)
-
- def save_data(self):
- """Saves the json"""
- dataIO.save_json(self.file_path, self.the_data)
-
- async def adj_roles(self, server, author, member: discord.Member=None, rrole_names=[], arole_names=[]):
- # Thank you SML for the addrole code
- # https://github.com/smlbiobot/SML-Cogs/tree/master/mm
-
- rroles = [r for r in server.roles if r.name in rrole_names]
- aroles = [r for r in server.roles if r.name in arole_names]
- try:
- await self.bot.add_roles(member, *aroles)
- await asyncio.sleep(0.5)
- await self.bot.remove_roles(member, *rroles)
- await asyncio.sleep(0.5)
-
- except discord.Forbidden:
- await self.bot.say(
- "{} does not have permission to edit {}’s roles.".format(
- author.display_name, member.display_name))
-
- except discord.HTTPException:
- await self.bot.say(
- "Failed to adjust roles.")
- except:
- await self.bot.say("Unknown Exception")
-
-
-
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def iresort(self, ctx, member: discord.Member=None):
- """Sends someone on vacation!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Member", "Immortal", "Eternal", "Phantom", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest"]
- arole_names = ["Resort"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Resort" in [r.name for r in member.roles]:
- await self.bot.say("You are being sent on Vacation! :tada:" +
- "Please relocate to Immortal Resort (#889L92UQ) when you find the time.")
- await self.bot.send_message(member, "You are being sent on Vacation! :tada: Please relocate " +
- "to Immortal Resort (#889L92UQ) when you find the time.\n" +
- "You'll have limited access to the server until you rejoin a main clan")
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def icrypt(self, ctx, member: discord.Member=None):
- """Sends someone to Crypt!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Immortal", "Eternal", "Ghost", "Phantom", "Revenant", "Undead", "Relocate", "Guest", "Resort"]
- arole_names = ["Member", "Crypt"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Crypt" in [r.name for r in member.roles]:
- await self.bot.say("Success")
- await self.send_welcome(member)
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def irevenant(self, ctx, member: discord.Member=None):
- """Sends someone to Revenant!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Immortal", "Eternal", "Ghost", "Phantom", "Undead", "Crypt", "Relocate", "Guest", "Resort"]
- arole_names = ["Member", "Revenant"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Revenant" in [r.name for r in member.roles]:
- await self.bot.say("Success")
- await self.send_welcome(member)
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def iundead(self, ctx, member: discord.Member=None):
- """Sends someone to Undead!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Immortal", "Eternal", "Ghost", "Phantom", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
- arole_names = ["Member", "Undead"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Undead" in [r.name for r in member.roles]:
- await self.bot.say("Success")
- await self.send_welcome(member)
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def iphantom(self, ctx, member: discord.Member=None):
- """Sends someone to Phantom!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Immortal", "Eternal", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
- arole_names = ["Member", "Phantom"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Phantom" in [r.name for r in member.roles]:
- await self.bot.say("Success")
- await self.send_welcome(member)
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def ieternal(self, ctx, member: discord.Member=None):
- """Sends someone to Eternal!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Immortal", "Phantom", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
- arole_names = ["Member", "Eternal"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Eternal" in [r.name for r in member.roles]:
- await self.bot.say("Success")
- await self.send_welcome(member)
-
- @commands.command(pass_context=True, no_pm=True)
- @checks.mod_or_permissions(manage_roles=True)
- async def iimmortal(self, ctx, member: discord.Member=None):
- """Sends someone to Immortal!"""
-
- if member is None:
- await self.bot.send_cmd_help(ctx)
- else:
- server = ctx.message.server
- author = ctx.message.author
- role_names = ["Eternal", "Phantom", "Ghost", "Undead", "Revenant", "Crypt", "Relocate", "Guest", "Resort"]
- arole_names = ["Member", "Immortal"]
- await self.adj_roles(server, author, member, role_names, arole_names)
- if "Immortal" in [r.name for r in member.roles]:
- await self.bot.say("Success")
- await self.send_welcome(member)
-
- @commands.group(aliases=['setimmortal'], pass_context=True, no_pm=True)
- @checks.mod_or_permissions(administrator=True)
- async def immortalset(self, ctx):
- """Adjust immortal settings"""
-
- server = ctx.message.server
- if server.id not in self.the_data:
- self.the_data[server.id] = {}
- self.save_data()
-
- if ctx.invoked_subcommand is None:
- await self.bot.send_cmd_help(ctx)
-
- @immortalset.command(pass_context=True, no_pm=True)
- async def welcomechannel(self, ctx):
- server = ctx.message.server
- if 'WELCOMECHANNEL' not in self.the_data[server.id]:
- self.the_data[server.id]['WELCOMECHANNEL'] = ''
-
- self.the_data[server.id]['WELCOMECHANNEL'] = ctx.message.channel.id
- self.save_data()
- await self.bot.say("Welcome Channel set to "+ctx.message.channel.name)
-
- async def send_welcome(self, member):
- server = member.server
- if server.id in self.the_data:
- await self.bot.send_message(server.get_channel(self.the_data[server.id]['WELCOMECHANNEL']),
- "You now have access to the server, " + member.mention + "\n" +
- "Check " + server.get_channel("257557008662790145").mention + " & " +
- server.get_channel("257560603093106688").mention+" for clan rules etc.\n" +
- "We recommend turning all message notifications on for " + server.get_channel("257560603093106688").mention +
- " if you want to know when tourneys are posted and other important info.\n" +
- "You can also type `!help` for a list of bot commands/features.")
-
-# @immortalset.command(pass_context=True)
-# async def channel(self, ctx):
-# server = ctx.message.server
-# if 'channel' not in self.the_data[server.id]:
-# self.the_data[server.id]['channel'] = ''
-
-# self.the_data[server.id]['channel'] = ctx.message.channel.id
-# self.save_data()
-
-# async def _when_leave(self, member):
-# server = member.server
-# if server.id not in self.the_data:
-# return
-
-# await self.bot.say("YOU LEFT ME "+member.mention)
-# self.the_data[server.id]
-
-
-def check_folders():
- if not os.path.exists("data/Fox-Cogs"):
- print("Creating data/Fox-Cogs folder...")
- os.makedirs("data/Fox-Cogs")
-
- if not os.path.exists("data/Fox-Cogs/immortal"):
- print("Creating data/Fox-Cogs/immortal folder...")
- os.makedirs("data/Fox-Cogs/immortal")
-
-
-def check_files():
- if not dataIO.is_valid_json("data/Fox-Cogs/immortal/immortal.json"):
- dataIO.save_json("data/Fox-Cogs/immortal/immortal.json", {})
-
-
-def setup(bot):
- check_folders()
- check_files()
- q = Immortal(bot)
- bot.add_cog(q)
diff --git a/immortal/info.json b/immortal/info.json
deleted file mode 100644
index 8668a65..0000000
--- a/immortal/info.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "AUTHOR" : "Bobloy",
- "INSTALL_MSG" : "Thank you for installing Immortal Family Cog",
- "NAME" : "Immortal",
- "SHORT" : "Cog for a specific server, will not work on other servers",
- "DESCRIPTION" : "Cog specifically for the Immortal Family discord server. I do not recommend installing it",
- "TAGS" : ["fox", "bobloy", "utilities", "tools", "utility", "tool"],
- "HIDDEN" : false
-}
\ No newline at end of file
diff --git a/info.json b/info.json
index 713f4e5..4d8e5e1 100644
--- a/info.json
+++ b/info.json
@@ -1,7 +1,7 @@
{
"AUTHOR": "Bobloy",
- "INSTALL_MSG": "Thank you for installing Fox-Cogs by Bobloy",
- "NAME": "Fox-Cogs",
+ "INSTALL_MSG": "Thank you for installing Fox-V3 by Bobloy",
+ "NAME": "Fox-V3",
"SHORT": "Cogs by Bobloy",
"DESCRIPTION": "Cogs for RED Discord Bot by Bobloy"
}
\ No newline at end of file
diff --git a/leaver/__init__.py b/leaver/__init__.py
new file mode 100644
index 0000000..ed6689b
--- /dev/null
+++ b/leaver/__init__.py
@@ -0,0 +1,5 @@
+from .leaver import Leaver
+
+
+def setup(bot):
+ bot.add_cog(Leaver(bot))
diff --git a/leaver/info.json b/leaver/info.json
index a1a44c3..669689b 100644
--- a/leaver/info.json
+++ b/leaver/info.json
@@ -1,9 +1,20 @@
{
- "AUTHOR": "Bobloy",
- "INSTALL_MSG": "Thank you for installing leaver",
- "NAME": "Leaver",
- "SHORT": "Sends message on leave",
- "DESCRIPTION": "Keeps track of when people leave the server, and posts a message notifying",
- "TAGS": ["fox", "bobloy", "utilities", "tools", "tool"],
- "HIDDEN": false
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Keeps track of when people leave the server, and posts a message notifying",
+ "hidden": false,
+ "install_msg": "Thank you for installing Leaver. Get started with `[p]load leaver`, then `[p]help Leaver`",
+ "requirements": [],
+ "short": "Send message on leave",
+ "tags": [
+ "bobloy",
+ "utils",
+ "tools"
+ ]
}
\ No newline at end of file
diff --git a/leaver/leaver.py b/leaver/leaver.py
index a4a2b23..a74dc24 100644
--- a/leaver/leaver.py
+++ b/leaver/leaver.py
@@ -1,78 +1,45 @@
import discord
-import os
-from datetime import datetime
-from discord.ext import commands
-from .utils.dataIO import dataIO
-from .utils import checks
+from redbot.core import Config, checks, commands
+from redbot.core.commands import Context
+from typing import Any
+Cog: Any = getattr(commands, "Cog", object)
-class Leaver:
- """Creates a goodbye message when people leave"""
+
+class Leaver(Cog):
+ """
+ Creates a goodbye message when people leave
+ """
def __init__(self, bot):
self.bot = bot
- self.path = "data/Fox-Cogs/leaver"
- self.file_path = "data/Fox-Cogs/leaver/leaver.json"
- self.the_data = dataIO.load_json(self.file_path)
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_guild = {
+ "channel": ''
+ }
- def save_data(self):
- """Saves the json"""
- dataIO.save_json(self.file_path, self.the_data)
+ self.config.register_guild(**default_guild)
- @commands.group(aliases=['setleaver'], pass_context=True, no_pm=True)
+ @commands.group(aliases=['setleaver'])
@checks.mod_or_permissions(administrator=True)
async def leaverset(self, ctx):
"""Adjust leaver settings"""
-
- server = ctx.message.server
- if server.id not in self.the_data:
- self.the_data[server.id] = {}
- self.save_data()
-
-
if ctx.invoked_subcommand is None:
- await self.bot.send_cmd_help(ctx)
+ pass
- @leaverset.command(pass_context=True, no_pm=True)
- async def channel(self, ctx):
- server = ctx.message.server
- if 'CHANNEL' not in self.the_data[server.id]:
- self.the_data[server.id]['CHANNEL'] = ''
-
+ @leaverset.command()
+ async def channel(self, ctx: Context):
+ guild = ctx.guild
+ await self.config.guild(guild).channel.set(ctx.channel.id)
+ await ctx.send("Channel set to " + ctx.channel.name)
- self.the_data[server.id]['CHANNEL'] = ctx.message.channel.id
- self.save_data()
- await self.bot.say("Channel set to "+ctx.message.channel.name)
+ async def on_member_remove(self, member: discord.Member):
+ guild = member.guild
+ channel = await self.config.guild(guild).channel()
- async def when_leave(self, member):
- server = member.server
- if server.id in self.the_data:
- await self.bot.send_message(server.get_channel(self.the_data[server.id]['CHANNEL']),
- str(member) + "(*" + str(member.nick) +"*) has left the server!")
+ if channel != '':
+ channel = guild.get_channel(channel)
+ await channel.send(str(member) + "(*" + str(member.nick) + "*) has left the server!")
else:
- await self.bot.send_message(server.default_channel.id, str(member) + " (*" + str(member.nick) +"*) has left the server!")
-
-
-def check_folders():
- if not os.path.exists("data/Fox-Cogs"):
- print("Creating data/Fox-Cogs folder...")
- os.makedirs("data/Fox-Cogs")
-
- if not os.path.exists("data/Fox-Cogs/leaver"):
- print("Creating data/Fox-Cogs/leaver folder...")
- os.makedirs("data/Fox-Cogs/leaver")
-
-
-def check_files():
- if not dataIO.is_valid_json("data/Fox-Cogs/leaver/leaver.json"):
- dataIO.save_json("data/Fox-Cogs/leaver/leaver.json", {})
-
-
-def setup(bot):
- check_folders()
- check_files()
- q = Leaver(bot)
- bot.add_listener(q.when_leave, "on_member_remove")
- bot.add_cog(q)
-
\ No newline at end of file
+ pass
diff --git a/lovecalculator/__init__.py b/lovecalculator/__init__.py
new file mode 100644
index 0000000..5284684
--- /dev/null
+++ b/lovecalculator/__init__.py
@@ -0,0 +1,5 @@
+from .lovecalculator import LoveCalculator
+
+
+def setup(bot):
+ bot.add_cog(LoveCalculator(bot))
diff --git a/lovecalculator/info.json b/lovecalculator/info.json
new file mode 100644
index 0000000..20601b6
--- /dev/null
+++ b/lovecalculator/info.json
@@ -0,0 +1,23 @@
+{
+ "author": [
+ "Bobloy",
+ "SnappyDragon"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Calculate the love percentage for two users",
+ "hidden": false,
+ "install_msg": "Thank you for installing LoveCalculator. Love is in the air.\n Get started with `[p]load lovecalculator`, then `[p]help LoveCalculator`",
+ "requirements": [
+ "beautifulsoup4"
+ ],
+ "short": "Calculate love percentage",
+ "tags": [
+ "bobloy",
+ "fun",
+ "love"
+ ]
+}
\ No newline at end of file
diff --git a/lovecalculator/lovecalculator.py b/lovecalculator/lovecalculator.py
new file mode 100644
index 0000000..5430825
--- /dev/null
+++ b/lovecalculator/lovecalculator.py
@@ -0,0 +1,47 @@
+import aiohttp
+import discord
+from bs4 import BeautifulSoup
+from redbot.core import commands
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class LoveCalculator(Cog):
+ """Calculate the love percentage for two users!"""
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command(aliases=['lovecalc'])
+ async def lovecalculator(self, ctx: commands.Context, lover: discord.Member, loved: discord.Member):
+ """Calculate the love percentage!"""
+
+ x = lover.display_name
+ y = loved.display_name
+
+ url = 'https://www.lovecalculator.com/love.php?name1={}&name2={}'.format(x.replace(" ", "+"),
+ y.replace(" ", "+"))
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ soup_object = BeautifulSoup(await response.text(), "html.parser")
+ try:
+ description = soup_object.find('div', attrs={'class': 'result score'}).get_text().strip()
+ except:
+ description = 'Dr. Love is busy right now'
+
+ try:
+ z = description[:2]
+ z = int(z)
+ if z > 50:
+ emoji = '❤'
+ else:
+ emoji = '💔'
+ title = 'Dr. Love says that the love percentage for {} and {} is:'.format(x, y)
+ except:
+ emoji = ''
+ title = 'Dr. Love has left a note for you.'
+
+ description = emoji + ' ' + description + ' ' + emoji
+ em = discord.Embed(title=title, description=description, color=discord.Color.red())
+ await ctx.send(embed=em)
diff --git a/lseen/__init__.py b/lseen/__init__.py
new file mode 100644
index 0000000..716f34d
--- /dev/null
+++ b/lseen/__init__.py
@@ -0,0 +1,5 @@
+from .lseen import LastSeen
+
+
+def setup(bot):
+ bot.add_cog(LastSeen(bot))
diff --git a/lseen/info..json b/lseen/info..json
new file mode 100644
index 0000000..c5e5eec
--- /dev/null
+++ b/lseen/info..json
@@ -0,0 +1,20 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Keep track of when users were last seen online",
+ "hidden": false,
+ "install_msg": "Thank you for installing LastSeen. Get started with `[p]load lseen`, then `[p]help LastSeen`",
+ "requirements": ["python-dateutil"],
+ "short": "Last seen tracker",
+ "tags": [
+ "bobloy",
+ "utils",
+ "tools"
+ ]
+}
\ No newline at end of file
diff --git a/lseen/lseen.py b/lseen/lseen.py
new file mode 100644
index 0000000..6f59b63
--- /dev/null
+++ b/lseen/lseen.py
@@ -0,0 +1,86 @@
+from datetime import datetime
+
+import dateutil.parser
+import discord
+
+from redbot.core import Config
+from redbot.core.bot import Red
+from redbot.core import commands
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class LastSeen(Cog):
+ """
+ Report when a user was last seen online
+ """
+
+ online_status = discord.Status.online
+
+ offline_status = discord.Status.offline
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {
+ "enabled": True
+ }
+ default_member = {
+ "seen": None
+ }
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+ self.config.register_member(**default_member)
+
+ @staticmethod
+ def get_date_time(s):
+ d = dateutil.parser.parse(s)
+ return d
+
+ @commands.group(aliases=['setlseen'], name='lseenset')
+ async def lset(self, ctx: commands.Context):
+ """Change settings for lseen"""
+ if ctx.invoked_subcommand is None:
+ pass
+
+ @lset.command(name="toggle")
+ async def lset_toggle(self, ctx: commands.Context):
+ """Toggles tracking seen for this server"""
+ enabled = not await self.config.guild(ctx.guild).enabled()
+ await self.config.guild(ctx.guild).enabled.set(
+ enabled)
+
+ await ctx.send(
+ "Seen for this server is now {}".format(
+ "Enabled" if enabled else "Disabled"))
+
+ @commands.command(aliases=['lastseen'])
+ async def lseen(self, ctx: commands.Context, member: discord.Member):
+ """
+ Just says the time the user was last seen
+ """
+
+ if member.status != self.offline_status:
+ last_seen = datetime.utcnow()
+ else:
+ last_seen = await self.config.member(member).seen()
+ if last_seen is None:
+ await ctx.send(embed=discord.Embed(description="I've never seen this user"))
+ return
+ last_seen = self.get_date_time(last_seen)
+
+ # embed = discord.Embed(
+ # description="{} was last seen at this date and time".format(member.display_name),
+ # timestamp=last_seen)
+
+ embed = discord.Embed(timestamp=last_seen)
+ await ctx.send(embed=embed)
+
+ async def on_member_update(self, before: discord.Member, after: discord.Member):
+ if before.status != self.offline_status and after.status == self.offline_status:
+ if not await self.config.guild(before.guild).enabled():
+ return
+ await self.config.member(before).seen.set(datetime.utcnow().isoformat())
diff --git a/nudity/__init__.py b/nudity/__init__.py
new file mode 100644
index 0000000..7d32df6
--- /dev/null
+++ b/nudity/__init__.py
@@ -0,0 +1,5 @@
+from .nudity import Nudity
+
+
+def setup(bot):
+ bot.add_cog(Nudity(bot))
diff --git a/nudity/info..json b/nudity/info..json
new file mode 100644
index 0000000..ba1594e
--- /dev/null
+++ b/nudity/info..json
@@ -0,0 +1,20 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Keep track of when users were last seen online",
+ "hidden": true,
+ "install_msg": "Thank you for installing LastSeen. Get started with `[p]load nudity`, then `[p]help Nudity`",
+ "requirements": ["nudepy"],
+ "short": "Last seen tracker",
+ "tags": [
+ "bobloy",
+ "utils",
+ "tools"
+ ]
+}
\ No newline at end of file
diff --git a/nudity/nudity.py b/nudity/nudity.py
new file mode 100644
index 0000000..be30ee4
--- /dev/null
+++ b/nudity/nudity.py
@@ -0,0 +1,45 @@
+from datetime import datetime
+
+import discord
+import nude
+from nude import Nude
+from redbot.core import Config
+from redbot.core import commands
+from redbot.core.bot import Red
+
+
+class Nudity:
+ """
+ V3 Cog Template
+ """
+
+ online_status = discord.Status.online
+
+ offline_status = discord.Status.offline
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+
+ default_guild = {
+ "enabled": False
+ }
+
+ self.config.register_guild(**default_guild)
+
+ @commands.command(aliases=['togglenudity'], name='nudity')
+ async def nudity(self, ctx: commands.Context):
+ """Toggle nude-checking on or off"""
+ is_on = await self.config.guild(ctx.guild).enabled()
+ await self.config.guild(ctx.guild).enabled.set(not is_on)
+ await ctx.send("Nude checking is now set to {}".format(not is_on))
+
+ async def on_message(self, message: discord.Message):
+ is_on = await self.config.guild(message.guild).enabled()
+ if not is_on:
+ return
+
+ if not message.attachments:
+ return
+
+
diff --git a/planttycoon/__init__.py b/planttycoon/__init__.py
new file mode 100644
index 0000000..c43d7b7
--- /dev/null
+++ b/planttycoon/__init__.py
@@ -0,0 +1,5 @@
+from .planttycoon import PlantTycoon
+
+
+def setup(bot):
+ bot.add_cog(PlantTycoon(bot))
diff --git a/planttycoon/data/badges.json b/planttycoon/data/badges.json
new file mode 100644
index 0000000..4a93b7f
--- /dev/null
+++ b/planttycoon/data/badges.json
@@ -0,0 +1,11 @@
+{
+ "badges": {
+ "Flower Power": {},
+ "Fruit Brute": {},
+ "Sporadic": {},
+ "Odd-pod": {},
+ "Greenfingers": {},
+ "Nobel Peas Prize": {},
+ "Annualsary": {}
+ }
+}
diff --git a/planttycoon/data/defaults.json b/planttycoon/data/defaults.json
new file mode 100644
index 0000000..cf9357f
--- /dev/null
+++ b/planttycoon/data/defaults.json
@@ -0,0 +1,22 @@
+{
+ "points": {
+ "buy": 5,
+ "add_health": 5,
+ "fertilize": 10,
+ "pruning": 20,
+ "pesticide": 25,
+ "growing": 5,
+ "damage": 25
+ },
+ "timers": {
+ "degradation": 1,
+ "completion": 1,
+ "notification": 5
+ },
+ "degradation": {
+ "base_degradation": 1.5
+ },
+ "notification": {
+ "max_health": 50
+ }
+}
diff --git a/planttycoon/data/notifications.json b/planttycoon/data/notifications.json
new file mode 100644
index 0000000..f0b68d1
--- /dev/null
+++ b/planttycoon/data/notifications.json
@@ -0,0 +1,7 @@
+{
+ "messages": [
+ "The soil seems dry, maybe you could give your plant some water?",
+ "Your plant seems a bit droopy. I would give it some fertilizer if I were you.",
+ "Your plant seems a bit too overgrown. You should probably trim it a bit."
+ ]
+}
diff --git a/planttycoon/data/plants.json b/planttycoon/data/plants.json
new file mode 100644
index 0000000..21cb05b
--- /dev/null
+++ b/planttycoon/data/plants.json
@@ -0,0 +1,690 @@
+{
+ "plants": [
+ {
+ "name": "Poppy",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/S4hjyUX.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Dandelion",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/emqnQP2.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Daisy",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/lcFq4AB.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Chrysanthemum",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/5jLtqWL.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Pansy",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/f7TgD1b.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Lavender",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/g3OmOSK.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Lily",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/0hzy7lO.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Petunia",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/rJm8ISv.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Sunflower",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/AzgzQK9.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Daffodil",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/pnCCRsH.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Clover",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/jNTgirw.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Tulip",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/kodIFjE.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Rose",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/sdTNiOH.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Aster",
+ "article": "an",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/1tN04Hl.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Aloe Vera",
+ "article": "an",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/WFAYIpx.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Orchid",
+ "article": "an",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/IQrQYDC.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600
+ },
+ {
+ "name": "Dragon Fruit Plant",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/pfngpDS.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Mango Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/ybR78Oc.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Lychee Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/w9LkfhX.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Durian Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/jh249fz.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Fig Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/YkhnpEV.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Jack Fruit Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/2D79TlA.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Prickly Pear Plant",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/GrcGAGj.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Pineapple Plant",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/VopYQtr.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Citron Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/zh7Dr23.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Cherimoya Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/H62gQK6.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Mangosteen Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/McNnMqa.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Guava Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/iy8WgPt.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Orange Tree",
+ "article": "an",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/lwjEJTm.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Apple Tree",
+ "article": "an",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/QI3UTR3.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Sapodilla Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/6BvO5Fu.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200
+ },
+ {
+ "name": "Franklin Tree",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/hoh17hp.jpg",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400
+ },
+ {
+ "name": "Parrot's Beak",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/lhSjfQY.jpg",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400
+ },
+ {
+ "name": "Koki'o",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/Dhw9ync.jpg",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400
+ },
+ {
+ "name": "Jade Vine",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/h4fJo2R.jpg",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400
+ },
+ {
+ "name": "Venus Fly Trap",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/NoSdxXh.jpg",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400
+ },
+ {
+ "name": "Chocolate Cosmos",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/4ArSekX.jpg",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400
+ },
+ {
+ "name": "Pizza Plant",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "http://i.imgur.com/ASZXr7C.png",
+ "health": 100,
+ "degradation": 1,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600
+ },
+ {
+ "name": "tba",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "tba",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600
+ },
+ {
+ "name": "Pirahna Plant",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "http://i.imgur.com/c03i9W7.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600
+ },
+ {
+ "name": "tba",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "tba",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600
+ },
+ {
+ "name": "Peashooter",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "https://i.imgur.com/Vo4v2Ry.png",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600
+ },
+ {
+ "name": "tba",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "tba",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400
+ },
+ {
+ "name": "Pikmin",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "http://i.imgur.com/sizf7hE.png",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400
+ },
+ {
+ "name": "Flora Colossus",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "http://i.imgur.com/9f5QzaW.jpg",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400
+ },
+ {
+ "name": "Plantera Bulb",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "https://i.imgur.com/ExqLLHO.png",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400
+ },
+ {
+ "name": "Chorus Tree",
+ "article": "an",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "https://i.imgur.com/tv2B72j.png",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400
+ },
+ {
+ "name": "Money Tree",
+ "article": "a",
+ "time": 35400,
+ "rarity": "legendary",
+ "image": "http://i.imgur.com/MIJQDLL.jpg",
+ "health": 100,
+ "degradation": 3,
+ "threshold": 110,
+ "badge": "Nobel Peas Prize",
+ "reward": 10800
+ },
+ {
+ "name": "Truffula Tree",
+ "article": "a",
+ "time": 35400,
+ "rarity": "legendary",
+ "image": "http://i.imgur.com/cFSmaHH.png",
+ "health": 100,
+ "degradation": 3,
+ "threshold": 110,
+ "badge": "Nobel Peas Prize",
+ "reward": 10800
+ },
+ {
+ "name": "Whomping Willow",
+ "article": "a",
+ "time": 35400,
+ "rarity": "legendary",
+ "image": "http://i.imgur.com/Ibwm2xY.jpg",
+ "health": 100,
+ "degradation": 3,
+ "threshold": 110,
+ "badge": "Nobel Peas Prize",
+ "reward": 10800
+ }
+ ],
+ "event": {
+ "January": {
+ "name": "Tanabata Tree",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/FD38JJj.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ },
+ "February": {
+ "name": "Chocolate Rose",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/Sqg6pcG.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ },
+ "March": {
+ "name": "Shamrock",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/kVig04M.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ },
+ "April": {
+ "name": "Easter Egg Eggplant",
+ "article": "an",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/5jltGQa.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ },
+ "October": {
+ "name": "Jack O' Lantern",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/efApsxG.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ },
+ "November": {
+ "name": "Mayflower",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/nntNtoL.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ },
+ "December": {
+ "name": "Holly",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/maDLmJC.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600
+ }
+ }
+}
diff --git a/planttycoon/data/products.json b/planttycoon/data/products.json
new file mode 100644
index 0000000..8d5b98d
--- /dev/null
+++ b/planttycoon/data/products.json
@@ -0,0 +1,42 @@
+{
+ "water": {
+ "cost": 5,
+ "health": 10,
+ "damage": 45,
+ "modifier": 0,
+ "category": "water",
+ "uses": 1
+ },
+ "manure": {
+ "cost": 20,
+ "health": 20,
+ "damage": 55,
+ "modifier": -0.035,
+ "category": "fertilizer",
+ "uses": 1
+ },
+ "vermicompost": {
+ "cost": 35,
+ "health": 30,
+ "damage": 60,
+ "modifier": -0.5,
+ "category": "fertilizer",
+ "uses": 1
+ },
+ "nitrates": {
+ "cost": 70,
+ "health": 60,
+ "damage": 75,
+ "modifier": -0.08,
+ "category": "fertilizer",
+ "uses": 1
+ },
+ "pruner": {
+ "cost": 500,
+ "health": 40,
+ "damage": 90,
+ "modifier": -0.065,
+ "category": "tool",
+ "uses": 10
+ }
+}
diff --git a/planttycoon/info.json b/planttycoon/info.json
new file mode 100644
index 0000000..32fe8e2
--- /dev/null
+++ b/planttycoon/info.json
@@ -0,0 +1,22 @@
+{
+ "author": [
+ "Bobloy",
+ "SnappyDragon",
+ "PaddoInWonderland"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Grow your own plants! Be sure to take care of it. Do `[p]gardening` to get started",
+ "hidden": false,
+ "install_msg": "Thank you for installing PlantTycoon. Check out all the commands with `[p]help PlantTycoon`",
+ "requirements": [],
+ "short": "Grow your own plants! Do `[p]gardening` to get started.",
+ "tags": [
+ "bobloy",
+ "games",
+ "environment"
+ ]
+}
\ No newline at end of file
diff --git a/planttycoon/planttycoon.py b/planttycoon/planttycoon.py
new file mode 100644
index 0000000..ae78ed9
--- /dev/null
+++ b/planttycoon/planttycoon.py
@@ -0,0 +1,1419 @@
+import asyncio
+import collections
+import datetime
+import time
+from random import choice
+
+import discord
+from redbot.core import commands, Config, bank
+from redbot.core.bot import Red
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class Gardener:
+ """Gardener class"""
+
+ def __init__(self, user: discord.User, config: Config):
+ self.user = user
+ self.config = config
+ self.badges = []
+ self.points = 0
+ self.products = {}
+ self.current = {}
+
+ def __str__(self):
+ return (
+ "Gardener named {}\n"
+ "Badges: {}\n"
+ "Points: {}\n"
+ "Products: {}\n"
+ "Current: {}".format(self.user, self.badges, self.points, self.products, self.current)
+ )
+
+ def __repr__(self):
+ return "{} - {} - {} - {} - {}".format(
+ self.user, self.badges, self.points, self.products, self.current
+ )
+
+ async def _load_config(self):
+ self.badges = await self.config.user(self.user).badges()
+ self.points = await self.config.user(self.user).points()
+ self.products = await self.config.user(self.user).products()
+ self.current = await self.config.user(self.user).current()
+
+ async def save_gardener(self):
+ await self.config.user(self.user).badges.set(self.badges)
+ await self.config.user(self.user).points.set(self.points)
+ await self.config.user(self.user).products.set(self.products)
+ await self.config.user(self.user).current.set(self.current)
+
+
+async def _die_in(gardener, degradation):
+ #
+ # Calculating how much time in minutes remains until the plant's health hits 0
+ #
+
+ return int(gardener.current["health"] / degradation.degradation)
+
+
+async def _grow_time(gardener):
+ #
+ # Calculating the remaining grow time for a plant
+ #
+
+ now = int(time.time())
+ then = gardener.current["timestamp"]
+ return (gardener.current["time"] - (now - then)) / 60
+
+
+async def _send_message(channel, message):
+ """Sendsa message"""
+
+ em = discord.Embed(description=message, color=discord.Color.green())
+ await channel.send(embed=em)
+
+
+async def _withdraw_points(gardener: Gardener, amount):
+ #
+ # Substract points from the gardener
+ #
+
+ if (gardener.points - amount) < 0:
+ return False
+ else:
+ gardener.points -= amount
+ return True
+
+
+class PlantTycoon(Cog):
+ """Grow your own plants! Be sure to take proper care of it."""
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=80108971101168412199111111110)
+
+ default_user = {"badges": [], "points": 0, "products": {}, "current": {}}
+
+ self.config.register_user(**default_user)
+
+ self.plants = {
+ "plants": [
+ {
+ "name": "Poppy",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/S4hjyUX.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Dandelion",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/emqnQP2.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Daisy",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/lcFq4AB.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Chrysanthemum",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/5jLtqWL.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Pansy",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/f7TgD1b.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Lavender",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/g3OmOSK.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Lily",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/0hzy7lO.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Petunia",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/rJm8ISv.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Sunflower",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/AzgzQK9.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Daffodil",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/pnCCRsH.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Clover",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/jNTgirw.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Tulip",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/kodIFjE.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Rose",
+ "article": "a",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/sdTNiOH.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Aster",
+ "article": "an",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/1tN04Hl.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Aloe Vera",
+ "article": "an",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/WFAYIpx.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Orchid",
+ "article": "an",
+ "time": 3600,
+ "rarity": "common",
+ "image": "http://i.imgur.com/IQrQYDC.jpg",
+ "health": 100,
+ "degradation": 0.625,
+ "threshold": 110,
+ "badge": "Flower Power",
+ "reward": 600,
+ },
+ {
+ "name": "Dragon Fruit Plant",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/pfngpDS.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Mango Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/ybR78Oc.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Lychee Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/w9LkfhX.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Durian Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/jh249fz.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Fig Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/YkhnpEV.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Jack Fruit Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/2D79TlA.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Prickly Pear Plant",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/GrcGAGj.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Pineapple Plant",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/VopYQtr.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Citron Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/zh7Dr23.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Cherimoya Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/H62gQK6.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Mangosteen Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/McNnMqa.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Guava Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/iy8WgPt.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Orange Tree",
+ "article": "an",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/lwjEJTm.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Apple Tree",
+ "article": "an",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/QI3UTR3.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Sapodilla Tree",
+ "article": "a",
+ "time": 5400,
+ "rarity": "uncommon",
+ "image": "http://i.imgur.com/6BvO5Fu.jpg",
+ "health": 100,
+ "degradation": 0.75,
+ "threshold": 110,
+ "badge": "Fruit Brute",
+ "reward": 1200,
+ },
+ {
+ "name": "Franklin Tree",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/hoh17hp.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400,
+ },
+ {
+ "name": "Parrot's Beak",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/lhSjfQY.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400,
+ },
+ {
+ "name": "Koki'o",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/Dhw9ync.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400,
+ },
+ {
+ "name": "Jade Vine",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/h4fJo2R.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400,
+ },
+ {
+ "name": "Venus Fly Trap",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/NoSdxXh.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400,
+ },
+ {
+ "name": "Chocolate Cosmos",
+ "article": "a",
+ "time": 7200,
+ "rarity": "rare",
+ "image": "http://i.imgur.com/4ArSekX.jpg",
+ "health": 100,
+ "degradation": 1.5,
+ "threshold": 110,
+ "badge": "Sporadic",
+ "reward": 2400,
+ },
+ {
+ "name": "Pizza Plant",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "http://i.imgur.com/ASZXr7C.png",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600,
+ },
+ # {
+ # "name": "tba",
+ # "article": "a",
+ # "time": 9000,
+ # "rarity": "super-rare",
+ # "image": "tba",
+ # "health": 100,
+ # "degradation": 1.5,
+ # "threshold": 110,
+ # "badge": "Odd-pod",
+ # "reward": 3600
+ # },
+ {
+ "name": "Piranha Plant",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "http://i.imgur.com/c03i9W7.jpg",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600,
+ },
+ # {
+ # "name": "tba",
+ # "article": "a",
+ # "time": 9000,
+ # "rarity": "super-rare",
+ # "image": "tba",
+ # "health": 100,
+ # "degradation": 1.5,
+ # "threshold": 110,
+ # "badge": "Odd-pod",
+ # "reward": 3600
+ # },
+ {
+ "name": "Peashooter",
+ "article": "a",
+ "time": 9000,
+ "rarity": "super-rare",
+ "image": "https://i.imgur.com/Vo4v2Ry.png",
+ "health": 100,
+ "degradation": 2,
+ "threshold": 110,
+ "badge": "Odd-pod",
+ "reward": 3600,
+ },
+ {
+ "name": "Eldergleam Tree",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "https://i.imgur.com/pnZYKZc.jpg",
+ "health": 100,
+ "degradation": 2.5,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400,
+ },
+ {
+ "name": "Pikmin",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "http://i.imgur.com/sizf7hE.png",
+ "health": 100,
+ "degradation": 2.5,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400,
+ },
+ {
+ "name": "Flora Colossus",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "http://i.imgur.com/9f5QzaW.jpg",
+ "health": 100,
+ "degradation": 2.5,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400,
+ },
+ {
+ "name": "Plantera Bulb",
+ "article": "a",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "https://i.imgur.com/ExqLLHO.png",
+ "health": 100,
+ "degradation": 2.5,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400,
+ },
+ {
+ "name": "Chorus Tree",
+ "article": "an",
+ "time": 10800,
+ "rarity": "epic",
+ "image": "https://i.imgur.com/tv2B72j.png",
+ "health": 100,
+ "degradation": 2.5,
+ "threshold": 110,
+ "badge": "Greenfingers",
+ "reward": 5400,
+ },
+ {
+ "name": "Money Tree",
+ "article": "a",
+ "time": 35400,
+ "rarity": "legendary",
+ "image": "http://i.imgur.com/MIJQDLL.jpg",
+ "health": 100,
+ "degradation": 8,
+ "threshold": 110,
+ "badge": "Nobel Peas Prize",
+ "reward": 10800,
+ },
+ {
+ "name": "Truffula Tree",
+ "article": "a",
+ "time": 35400,
+ "rarity": "legendary",
+ "image": "http://i.imgur.com/cFSmaHH.png",
+ "health": 100,
+ "degradation": 8,
+ "threshold": 110,
+ "badge": "Nobel Peas Prize",
+ "reward": 10800,
+ },
+ {
+ "name": "Whomping Willow",
+ "article": "a",
+ "time": 35400,
+ "rarity": "legendary",
+ "image": "http://i.imgur.com/Ibwm2xY.jpg",
+ "health": 100,
+ "degradation": 8,
+ "threshold": 110,
+ "badge": "Nobel Peas Prize",
+ "reward": 10800,
+ },
+ ],
+ "event": {
+ "January": {
+ "name": "Tanabata Tree",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/FD38JJj.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ "February": {
+ "name": "Chocolate Rose",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/Sqg6pcG.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ "March": {
+ "name": "Shamrock",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/kVig04M.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ "April": {
+ "name": "Easter Egg Eggplant",
+ "article": "an",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/5jltGQa.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ "October": {
+ "name": "Jack O' Lantern",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/efApsxG.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ "November": {
+ "name": "Mayflower",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/nntNtoL.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ "December": {
+ "name": "Holly",
+ "article": "a",
+ "time": 70800,
+ "rarity": "event",
+ "image": "http://i.imgur.com/maDLmJC.jpg",
+ "health": 100,
+ "degradation": 9,
+ "threshold": 110,
+ "badge": "Annualsary",
+ "reward": 21600,
+ },
+ },
+ }
+
+ self.products = {
+ "water": {
+ "cost": 5,
+ "health": 10,
+ "damage": 45,
+ "modifier": 0,
+ "category": "water",
+ "uses": 1,
+ },
+ "manure": {
+ "cost": 20,
+ "health": 20,
+ "damage": 55,
+ "modifier": -0.035,
+ "category": "fertilizer",
+ "uses": 1,
+ },
+ "vermicompost": {
+ "cost": 35,
+ "health": 30,
+ "damage": 60,
+ "modifier": -0.5,
+ "category": "fertilizer",
+ "uses": 1,
+ },
+ "nitrates": {
+ "cost": 70,
+ "health": 60,
+ "damage": 75,
+ "modifier": -0.08,
+ "category": "fertilizer",
+ "uses": 1,
+ },
+ "pruner": {
+ "cost": 500,
+ "health": 40,
+ "damage": 90,
+ "modifier": -0.065,
+ "category": "tool",
+ "uses": 10,
+ },
+ }
+
+ self.defaults = {
+ "points": {
+ "buy": 5,
+ "add_health": 5,
+ "fertilize": 10,
+ "pruning": 20,
+ "pesticide": 25,
+ "growing": 5,
+ "damage": 25,
+ },
+ "timers": {"degradation": 1, "completion": 1, "notification": 5},
+ "degradation": {"base_degradation": 1.5},
+ "notification": {"max_health": 50},
+ }
+
+ self.badges = {
+ "badges": {
+ "Flower Power": {},
+ "Fruit Brute": {},
+ "Sporadic": {},
+ "Odd-pod": {},
+ "Greenfingers": {},
+ "Nobel Peas Prize": {},
+ "Annualsary": {},
+ }
+ }
+
+ self.notifications = {
+ "messages": [
+ "The soil seems dry, maybe you could give your plant some water?",
+ "Your plant seems a bit droopy. I would give it some fertilizer if I were you.",
+ "Your plant seems a bit too overgrown. You should probably trim it a bit.",
+ ]
+ }
+
+ #
+ # Starting loops
+ #
+
+ self.completion_task = bot.loop.create_task(self.check_completion())
+ self.degradation_task = bot.loop.create_task(self.check_degradation())
+ self.notification_task = bot.loop.create_task(self.send_notification())
+
+ #
+ # Loading bank
+ #
+
+ # self.bank = bot.get_cog('Economy').bank
+
+ async def _gardener(self, user: discord.User) -> Gardener:
+
+ #
+ # This function returns an individual gardener namedtuple
+ #
+
+ g = Gardener(user, self.config)
+ await g._load_config()
+ return g
+
+ async def _degradation(self, gardener: Gardener):
+
+ #
+ # Calculating the rate of degradation per check_completion() cycle.
+ #
+
+ modifiers = sum(
+ [
+ self.products[product]["modifier"]
+ for product in gardener.products
+ if gardener.products[product] > 0
+ ]
+ )
+
+ degradation = (
+ 100
+ / (gardener.current["time"] / 60)
+ * (self.defaults["degradation"]["base_degradation"] + gardener.current["degradation"])
+ ) + modifiers
+
+ d = collections.namedtuple("degradation", "degradation time modifiers")
+
+ return d(degradation=degradation, time=gardener.current["time"], modifiers=modifiers)
+
+ # async def _get_member(self, user_id):
+ #
+ # #
+ # # Return a member object
+ # #
+ #
+ # return discord.User(id=user_id) # I made it a string just to be sure
+ #
+ # async def _send_notification(self, user_id, message):
+ #
+ # #
+ # # Sends a Direct Message to the gardener
+ # #
+ #
+ # member = await self._get_member(user_id)
+ # em = discord.Embed(description=message, color=discord.Color.green())
+ # await self.bot.send_message(member, embed=em)
+
+ async def _add_health(self, channel, gardener: Gardener, product, product_category):
+
+ #
+ # The function to add health
+ #
+
+ product = product.lower()
+ product_category = product_category.lower()
+ if product in self.products and self.products[product]["category"] == product_category:
+ if product in gardener.products:
+ if gardener.products[product] > 0:
+ gardener.current["health"] += self.products[product]["health"]
+ gardener.products[product] -= 1
+ if gardener.products[product] == 0:
+ del gardener.products[product.lower()]
+ if product_category == "water":
+ emoji = ":sweat_drops:"
+ elif product_category == "fertilizer":
+ emoji = ":poop:"
+ # elif product_category == "tool":
+ else:
+ emoji = ":scissors:"
+ message = "Your plant got some health back! {}".format(emoji)
+ if gardener.current["health"] > gardener.current["threshold"]:
+ gardener.current["health"] -= self.products[product]["damage"]
+ if product_category == "tool":
+ damage_msg = "You used {} too many times!".format(product)
+ else:
+ damage_msg = "You gave too much of {}.".format(product)
+ message = "{} Your plant lost some health. :wilted_rose:".format(
+ damage_msg
+ )
+ gardener.points += self.defaults["points"]["add_health"]
+ await gardener.save_gardener()
+ else:
+ message = "You have no {}. Go buy some!".format(product)
+ else:
+ if product_category == "tool":
+ message = "You have don't have a {}. Go buy one!".format(product)
+ else:
+ message = "You have no {}. Go buy some!".format(product)
+ else:
+ message = "Are you sure you are using {}?".format(product_category)
+
+ if product_category == "water":
+ emcolor = discord.Color.blue()
+ elif product_category == "fertilizer":
+ emcolor = discord.Color.dark_gold()
+ # elif product_category == "tool":
+ else:
+ emcolor = discord.Color.dark_grey()
+
+ em = discord.Embed(description=message, color=emcolor)
+ await channel.send(embed=em)
+
+ @commands.group(name="gardening", autohelp=False)
+ async def _gardening(self, ctx: commands.Context):
+ """Gardening commands."""
+ if ctx.invoked_subcommand is None:
+ prefix = ctx.prefix
+
+ title = "**Welcome to Plant Tycoon.**\n"
+ description = """'Grow your own plant. Be sure to take proper care of yours.\n
+ If it successfully grows, you get a reward.\n
+ As you nurture your plant, you gain Thneeds which can be exchanged for credits.\n\n
+ **Commands**\n\n
+ ``{0}gardening seed``: Plant a seed inside the earth.\n
+ ``{0}gardening profile``: Check your gardening profile.\n
+ ``{0}gardening plants``: Look at the list of the available plants.\n
+ ``{0}gardening plant``: Look at the details of a plant.\n
+ ``{0}gardening state``: Check the state of your plant.\n
+ ``{0}gardening buy``: Buy gardening supplies.\n
+ ``{0}gardening convert``: Exchange Thneeds for credits.\n
+ ``{0}shovel``: Shovel your plant out.\n
+ ``{0}water``: Water your plant.\n
+ ``{0}fertilize``: Fertilize the soil.\n
+ ``{0}prune``: Prune your plant.\n"""
+
+ em = discord.Embed(
+ title=title, description=description.format(prefix), color=discord.Color.green()
+ )
+ em.set_thumbnail(url="https://image.prntscr.com/image/AW7GuFIBSeyEgkR2W3SeiQ.png")
+ em.set_footer(
+ text="This cog was made by SnappyDragon18 and PaddoInWonderland. Inspired by The Lorax (2012)."
+ )
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="seed")
+ async def _seed(self, ctx: commands.Context):
+ """Plant a seed inside the earth."""
+ author = ctx.author
+ # server = context.message.server
+ # if author.id not in self.gardeners:
+ # self.gardeners[author.id] = {}
+ # self.gardeners[author.id]['current'] = False
+ # self.gardeners[author.id]['points'] = 0
+ # self.gardeners[author.id]['badges'] = []
+ # self.gardeners[author.id]['products'] = {}
+ gardener = await self._gardener(author)
+
+ if not gardener.current:
+ d = datetime.date.today()
+ month = d.month
+
+ #
+ # Event Plant Check start
+ #
+ plant_options = self.plants["plants"]
+
+ if month == 1:
+ plant_options.append(self.plants["event"]["January"])
+ elif month == 2:
+ plant_options.append(self.plants["event"]["February"])
+ elif month == 3:
+ plant_options.append(self.plants["event"]["March"])
+ elif month == 4:
+ plant_options.append(self.plants["event"]["April"])
+ elif month == 10:
+ plant_options.append(self.plants["event"]["October"])
+ elif month == 11:
+ plant_options.append(self.plants["event"]["November"])
+ elif month == 12:
+ plant_options.append(self.plants["event"]["December"])
+
+
+ #
+ # Event Plant Check end
+ #
+
+ plant = choice(plant_options)
+ plant["timestamp"] = int(time.time())
+ # index = len(self.plants["plants"]) - 1
+ # del [self.plants["plants"][index]]
+ message = (
+ "During one of your many heroic adventures, you came across a mysterious bag that said "
+ '"pick one". To your surprise it had all kinds of different seeds in them. '
+ "And now that you're home, you want to plant it. "
+ "You went to a local farmer to identify the seed, and the farmer "
+ "said it was {} **{} ({})** seed.\n\n"
+ "Take good care of your seed and water it frequently. "
+ "Once it blooms, something nice might come from it. "
+ "If it dies, however, you will get nothing.".format(
+ plant["article"], plant["name"], plant["rarity"]
+ )
+ )
+ if "water" not in gardener.products:
+ gardener.products["water"] = 0
+ gardener.products["water"] += 5
+ gardener.current = plant
+ await gardener.save_gardener()
+
+ em = discord.Embed(description=message, color=discord.Color.green())
+ else:
+ plant = gardener.current
+ message = "You're already growing {} **{}**, silly.".format(
+ plant["article"], plant["name"]
+ )
+ em = discord.Embed(description=message, color=discord.Color.green())
+
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="profile")
+ async def _profile(self, ctx: commands.Context, *, member: discord.Member = None):
+ """Check your gardening profile."""
+ if member:
+ author = member
+ else:
+ author = ctx.author
+
+ gardener = await self._gardener(author)
+ em = discord.Embed(color=discord.Color.green()) # , description='\a\n')
+ avatar = author.avatar_url if author.avatar else author.default_avatar_url
+ em.set_author(name="Gardening profile of {}".format(author.name), icon_url=avatar)
+ em.add_field(name="**Thneeds**", value=str(gardener.points))
+ if not gardener.current:
+ em.add_field(name="**Currently growing**", value="None")
+ else:
+ em.set_thumbnail(url=gardener.current["image"])
+ em.add_field(
+ name="**Currently growing**",
+ value="{0} ({1:.2f}%)".format(
+ gardener.current["name"], gardener.current["health"]
+ ),
+ )
+ if not gardener.badges:
+ em.add_field(name="**Badges**", value="None")
+ else:
+ badges = ""
+ for badge in gardener.badges:
+ badges += "{}\n".format(badge.capitalize())
+ em.add_field(name="**Badges**", value=badges)
+ if not gardener.products:
+ em.add_field(name="**Products**", value="None")
+ else:
+ products = ""
+ for product in gardener.products:
+ products += "{} ({}) {}\n".format(
+ product.capitalize(),
+ gardener.products[product] / self.products[product.lower()]["uses"],
+ self.products[product]["modifier"],
+ )
+ em.add_field(name="**Products**", value=products)
+ if gardener.current:
+ degradation = await self._degradation(gardener)
+ die_in = await _die_in(gardener, degradation)
+ to_grow = await _grow_time(gardener)
+ em.set_footer(
+ text="Total degradation: {0:.2f}% / {1} min (100 / ({2} / 60) * (BaseDegr {3:.2f} + PlantDegr {4:.2f}))"
+ " + ModDegr {5:.2f}) Your plant will die in {6} minutes "
+ "and {7:.1f} minutes to go for flowering.".format(
+ degradation.degradation,
+ self.defaults["timers"]["degradation"],
+ degradation.time,
+ self.defaults["degradation"]["base_degradation"],
+ gardener.current["degradation"],
+ degradation.modifiers,
+ die_in,
+ to_grow,
+ )
+ )
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="plants")
+ async def _plants(self, ctx):
+ """Look at the list of the available plants."""
+ tick = ""
+ tock = ""
+ tick_tock = 0
+ for plant in self.plants["plants"]:
+ if tick_tock == 0:
+ tick += "**{}**\n".format(plant["name"])
+ tick_tock = 1
+ else:
+ tock += "**{}**\n".format(plant["name"])
+ tick_tock = 0
+ em = discord.Embed(title="All plants that are growable", color=discord.Color.green())
+ em.add_field(name="\a", value=tick)
+ em.add_field(name="\a", value=tock)
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="plant")
+ async def _plant(self, ctx: commands.Context, *plant):
+ """Look at the details of a plant."""
+ plant = " ".join(plant)
+ t = False
+ for p in self.plants["plants"]:
+ if p["name"].lower() == plant.lower():
+ plant = p
+ t = True
+ break
+ if t:
+ em = discord.Embed(
+ title="Plant statistics of {}".format(plant["name"]), color=discord.Color.green()
+ )
+ em.set_thumbnail(url=plant["image"])
+ em.add_field(name="**Name**", value=plant["name"])
+ em.add_field(name="**Rarity**", value=plant["rarity"].capitalize())
+ em.add_field(name="**Grow Time**", value="{0:.1f} minutes".format(plant["time"] / 60))
+ em.add_field(name="**Damage Threshold**", value="{}%".format(plant["threshold"]))
+ em.add_field(name="**Badge**", value=plant["badge"])
+ em.add_field(name="**Reward**", value="{} τ".format(plant["reward"]))
+ else:
+ message = "What plant?"
+ em = discord.Embed(description=message, color=discord.Color.red())
+ await ctx.send_help()
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="state")
+ async def _state(self, ctx):
+ """Check the state of your plant."""
+ author = ctx.author
+ gardener = await self._gardener(author)
+ if not gardener.current:
+ message = "You're currently not growing a plant."
+ em_color = discord.Color.red()
+ else:
+ plant = gardener.current
+ degradation = await self._degradation(gardener)
+ die_in = await _die_in(gardener, degradation)
+ to_grow = await _grow_time(gardener)
+ message = (
+ "You're growing {0} **{1}**. "
+ "Its health is **{2:.2f}%** and still has to grow for **{3:.1f}** minutes. "
+ "It is losing **{4:.2f}%** per minute and will die in **{5:.1f}** minutes.".format(
+ plant["article"],
+ plant["name"],
+ plant["health"],
+ to_grow,
+ degradation.degradation,
+ die_in,
+ )
+ )
+ em_color = discord.Color.green()
+ em = discord.Embed(description=message, color=em_color)
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="buy")
+ async def _buy(self, ctx, product=None, amount: int = 1):
+ """Buy gardening supplies."""
+ author = ctx.author
+ if product is None:
+ em = discord.Embed(
+ title="All gardening supplies that you can buy:", color=discord.Color.green()
+ )
+ for product in self.products:
+ em.add_field(
+ name="**{}**".format(product.capitalize()),
+ value="Cost: {} τ\n+{} health\n-{}% damage\nUses: {}\nCategory: {}".format(
+ self.products[product]["cost"],
+ self.products[product]["health"],
+ self.products[product]["damage"],
+ self.products[product]["uses"],
+ self.products[product]["category"],
+ ),
+ )
+ await ctx.send(embed=em)
+ else:
+ if amount <= 0:
+ message = "Invalid amount! Must be greater than 1"
+ else:
+ gardener = await self._gardener(author)
+ if product.lower() in self.products and amount > 0:
+ cost = self.products[product.lower()]["cost"] * amount
+ withdraw_points = await _withdraw_points(gardener, cost)
+ if withdraw_points:
+ if product.lower() not in gardener.products:
+ gardener.products[product.lower()] = 0
+ gardener.products[product.lower()] += amount
+ gardener.products[product.lower()] += (
+ amount * self.products[product.lower()]["uses"]
+ )
+ await gardener.save_gardener()
+ message = "You bought {}.".format(product.lower())
+ else:
+ message = "You don't have enough Thneeds. You have {}, but need {}.".format(
+ gardener.points, self.products[product.lower()]["cost"] * amount
+ )
+ else:
+ message = "I don't have this product."
+ em = discord.Embed(description=message, color=discord.Color.green())
+ await ctx.send(embed=em)
+
+ @_gardening.command(name="convert")
+ async def _convert(self, ctx: commands.Context, amount: int):
+ """Exchange Thneeds for credits."""
+ author = ctx.author
+ gardener = await self._gardener(author)
+
+ withdraw_points = await _withdraw_points(gardener, amount)
+ plural = ""
+ if amount > 0:
+ plural = "s"
+ if withdraw_points:
+ await bank.deposit_credits(author, amount)
+ message = "{} Thneed{} successfully exchanged for credits.".format(amount, plural)
+ await gardener.save_gardener()
+ else:
+ message = "You don't have enough Thneed{}. " "You have {}, but need {}.".format(
+ plural, gardener.points, amount
+ )
+
+ em = discord.Embed(description=message, color=discord.Color.green())
+ await ctx.send(embed=em)
+
+ @commands.cooldown(1, 60 * 10, commands.BucketType.user)
+ @commands.command(name="shovel")
+ async def _shovel(self, ctx: commands.Context):
+ """Shovel your plant out."""
+ author = ctx.author
+ gardener = await self._gardener(author)
+ if not gardener.current:
+ message = "You're currently not growing a plant."
+ else:
+ gardener.current = {}
+ message = "You sucessfuly shovelled your plant out."
+ if gardener.points < 0:
+ gardener.points = 0
+ await gardener.save_gardener()
+
+ em = discord.Embed(description=message, color=discord.Color.dark_grey())
+ await ctx.send(embed=em)
+
+ @commands.command(name="water")
+ async def _water(self, ctx):
+ """Water your plant."""
+ author = ctx.author
+ channel = ctx.channel
+ gardener = await self._gardener(author)
+ product = "water"
+ product_category = "water"
+ if not gardener.current:
+ message = "You're currently not growing a plant."
+ await _send_message(channel, message)
+ else:
+ await self._add_health(channel, gardener, product, product_category)
+
+ @commands.command(name="fertilize")
+ async def _fertilize(self, ctx, fertilizer):
+ """Fertilize the soil."""
+ gardener = await self._gardener(ctx.author)
+ channel = ctx.channel
+ product = fertilizer
+ product_category = "fertilizer"
+ if not gardener.current:
+ message = "You're currently not growing a plant."
+ await _send_message(channel, message)
+ else:
+ await self._add_health(channel, gardener, product, product_category)
+
+ @commands.command(name="prune")
+ async def _prune(self, ctx):
+ """Prune your plant."""
+ gardener = await self._gardener(ctx.author)
+ channel = ctx.channel
+ product = "pruner"
+ product_category = "tool"
+ if not gardener.current:
+ message = "You're currently not growing a plant."
+ await _send_message(channel, message)
+ else:
+ await self._add_health(channel, gardener, product, product_category)
+
+ async def check_degradation(self):
+ while "PlantTycoon" in self.bot.cogs:
+ users = await self.config.all_users()
+ for user_id in users:
+ user = self.bot.get_user(user_id)
+ gardener = await self._gardener(user)
+ if gardener.current:
+ degradation = await self._degradation(gardener)
+ gardener.current["health"] -= degradation.degradation
+ gardener.points += self.defaults["points"]["growing"]
+ await gardener.save_gardener()
+ await asyncio.sleep(self.defaults["timers"]["degradation"] * 60)
+
+ async def check_completion(self):
+ while "PlantTycoon" in self.bot.cogs:
+ now = int(time.time())
+ users = await self.config.all_users()
+ for user_id in users:
+ message = None
+ user = self.bot.get_user(user_id)
+ gardener = await self._gardener(user)
+ if gardener.current:
+ then = gardener.current["timestamp"]
+ health = gardener.current["health"]
+ grow_time = gardener.current["time"]
+ badge = gardener.current["badge"]
+ reward = gardener.current["reward"]
+ if (now - then) > grow_time:
+ gardener.points += reward
+ if badge not in gardener.badges:
+ gardener.badges.append(badge)
+ message = (
+ "Your plant made it! "
+ "You are rewarded with the **{}** badge and you have received **{}** Thneeds.".format(
+ badge, reward
+ )
+ )
+ if health < 0:
+ message = "Your plant died!"
+ if message is not None:
+ await user.send(message)
+ gardener.current = {}
+ await gardener.save_gardener()
+ await asyncio.sleep(self.defaults["timers"]["completion"] * 60)
+
+ async def send_notification(self):
+ while "PlantTycoon" in self.bot.cogs:
+ users = await self.config.all_users()
+ for user_id in users:
+ user = self.bot.get_user(user_id)
+ gardener = await self._gardener(user)
+ if gardener.current:
+ health = gardener.current["health"]
+ if health < self.defaults["notification"]["max_health"]:
+ message = choice(self.notifications["messages"])
+ await user.send(message)
+ await asyncio.sleep(self.defaults["timers"]["notification"] * 60)
+
+ def __unload(self):
+ self.completion_task.cancel()
+ self.degradation_task.cancel()
+ self.notification_task.cancel()
diff --git a/qrinvite/__init__.py b/qrinvite/__init__.py
new file mode 100644
index 0000000..a91023a
--- /dev/null
+++ b/qrinvite/__init__.py
@@ -0,0 +1,5 @@
+from .qrinvite import QRInvite
+
+
+def setup(bot):
+ bot.add_cog(QRInvite(bot))
diff --git a/qrinvite/info..json b/qrinvite/info..json
new file mode 100644
index 0000000..0db8d11
--- /dev/null
+++ b/qrinvite/info..json
@@ -0,0 +1,23 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Create a QR code invite for the server",
+ "hidden": false,
+ "install_msg": "Thank you for installing QRInvite! Get started with `[p]load qrinvite`, then `[p]help QRInvite`",
+ "requirements": [
+ "MyQR"
+ ],
+ "short": "Create a QR code invite",
+ "tags": [
+ "bobloy",
+ "tools",
+ "qr",
+ "code"
+ ]
+}
\ No newline at end of file
diff --git a/qrinvite/qrinvite.py b/qrinvite/qrinvite.py
new file mode 100644
index 0000000..bbeaeca
--- /dev/null
+++ b/qrinvite/qrinvite.py
@@ -0,0 +1,87 @@
+import pathlib
+
+import aiohttp
+import discord
+from MyQR import myqr
+from PIL import Image
+from redbot.core import Config, commands
+from redbot.core.bot import Red
+from redbot.core.data_manager import cog_data_path
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class QRInvite(Cog):
+ """
+ V3 Cog Template
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {}
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @commands.command()
+ async def qrinvite(self, ctx: commands.Context, invite: str = None, colorized: bool = False, image_url: str = None):
+ """
+ Create a custom QR code invite for this server
+ """
+ if invite is None:
+ try:
+ invite = await ctx.channel.create_invite()
+ except discord.Forbidden:
+ try:
+ invite = await ctx.channel.invites()
+ invite = invite[0]
+ except discord.Forbidden:
+ await ctx.send("No permission to get an invite, please provide one")
+ return
+ invite = invite.code
+
+ if image_url is None:
+ image_url = ctx.guild.icon_url
+
+ if image_url == "": # Still
+ await ctx.send(
+ "Could not get an image, please provide one. *(`{}help qrinvite` for details)*".format(ctx.prefix))
+ return
+
+ extension = pathlib.Path(image_url).parts[-1].replace('.', '?').split('?')[1]
+
+ path: pathlib.Path = cog_data_path(self)
+ image_path = path / (ctx.guild.icon + "." + extension)
+ async with aiohttp.ClientSession() as session:
+ async with session.get(image_url) as response:
+ image = await response.read()
+
+ with image_path.open("wb") as file:
+ file.write(image)
+
+ if extension == "webp":
+ new_path = convert_png(str(image_path))
+ else:
+ new_path = str(image_path)
+
+ myqr.run(invite, picture=new_path, save_name=ctx.guild.icon + "_qrcode.png",
+ save_dir=str(cog_data_path(self)), colorized=colorized, )
+
+ png_path: pathlib.Path = path / (ctx.guild.icon + "_qrcode.png")
+ with png_path.open("rb") as png_fp:
+ await ctx.send(file=discord.File(png_fp.read(), "qrcode.png"))
+
+
+def convert_png(path):
+ im = Image.open(path)
+ im.load()
+ alpha = im.split()[-1]
+ im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)
+ mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
+ im.paste(255, mask)
+ new_path = path.replace(".webp", ".png")
+ im.save(new_path, transparency=255)
+ return new_path
diff --git a/reactrestrict/info.json b/reactrestrict/info.json
index cf33705..2695630 100644
--- a/reactrestrict/info.json
+++ b/reactrestrict/info.json
@@ -2,7 +2,7 @@
"author" : ["Bobloy"],
"bot_version" : [3,0,0],
"description" : "Cog to prevent reactions on specific messages from certain users",
- "hidden" : false,
+ "hidden" : true,
"install_msg" : "Thank you for installing ReactRestrict.",
"requirements" : [],
"short" : "[Incomplete] Prevent reactions",
diff --git a/reactrestrict/reactrestrict.py b/reactrestrict/reactrestrict.py
index 0141ab3..24133a5 100644
--- a/reactrestrict/reactrestrict.py
+++ b/reactrestrict/reactrestrict.py
@@ -1,11 +1,12 @@
-import asyncio
from typing import List, Union
import discord
-from discord.ext import commands
-
from redbot.core import Config
+from redbot.core import commands
from redbot.core.bot import Red
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
class ReactRestrictCombo:
@@ -15,8 +16,8 @@ class ReactRestrictCombo:
def __eq__(self, other: "ReactRestrictCombo"):
return (
- self.message_id == other.message_id and
- self.role_id == other.role_id
+ self.message_id == other.message_id and
+ self.role_id == other.role_id
)
def to_json(self):
@@ -33,7 +34,7 @@ class ReactRestrictCombo:
)
-class ReactRestrict:
+class ReactRestrict(Cog):
"""
Prevent specific roles from reacting to specific messages
"""
@@ -79,14 +80,10 @@ class ReactRestrict:
async def add_reactrestrict(self, message_id: int, role: discord.Role):
"""
Adds a react|role combo.
-
- :param int message_id:
- :param str or int emoji:
- :param discord.Role role:
"""
# is_custom = True
# if isinstance(emoji, str):
- # is_custom = False
+ # is_custom = False
combo = ReactRestrictCombo(message_id, role.id)
@@ -98,10 +95,10 @@ class ReactRestrict:
async def remove_react(self, message_id: int, role: discord.Role):
"""
- Removes a given reaction.
+ Removes a given reaction
- :param int message_id:
- :param str or int emoji:
+ :param message_id:
+ :param role:
:return:
"""
current_combos = await self.combo_list()
@@ -112,14 +109,13 @@ class ReactRestrict:
if to_keep != current_combos:
await self.set_combo_list(to_keep)
- async def has_reactrestrict_combo(self, message_id: int)\
+ async def has_reactrestrict_combo(self, message_id: int) \
-> (bool, List[ReactRestrictCombo]):
"""
- Determines if there is an existing role combo for a given message
+ Determines if there is an existing role combo for a given message
and emoji ID.
- :param int message_id:
- :param str or int emoji:
+ :param message_id:
:return:
"""
if not await self.is_registered(message_id):
@@ -172,27 +168,23 @@ class ReactRestrict:
raise LookupError("No role found.")
return role
-
- async def _get_message_from_channel(self, channel_id: int, message_id: int)\
+
+ async def _get_message_from_channel(self, channel_id: int, message_id: int) \
-> Union[discord.Message, None]:
"""
Tries to find a message by ID in the current guild context.
-
- :param ctx:
- :param message_id:
- :return:
"""
channel = self.bot.get_channel(channel_id)
try:
return await channel.get_message(message_id)
except discord.NotFound:
pass
- except AttributeError: # VoiceChannel object has no attribute 'get_message'
+ except AttributeError: # VoiceChannel object has no attribute 'get_message'
pass
return None
-
- async def _get_message(self, ctx: commands.Context, message_id: int)\
+
+ async def _get_message(self, ctx: commands.Context, message_id: int) \
-> Union[discord.Message, None]:
"""
Tries to find a message by ID in the current guild context.
@@ -206,12 +198,10 @@ class ReactRestrict:
return await channel.get_message(message_id)
except discord.NotFound:
pass
- except AttributeError: # VoiceChannel object has no attribute 'get_message'
+ except AttributeError: # VoiceChannel object has no attribute 'get_message'
pass
except discord.Forbidden: # No access to channel, skip
pass
-
-
return None
@@ -221,7 +211,7 @@ class ReactRestrict:
Base command for this cog. Check help for the commands list.
"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
+ pass
@reactrestrict.command()
async def add(self, ctx: commands.Context, message_id: int, *, role: discord.Role):
@@ -235,18 +225,18 @@ class ReactRestrict:
return
# try:
- # emoji, actual_emoji = await self._wait_for_emoji(ctx)
+ # emoji, actual_emoji = await self._wait_for_emoji(ctx)
# except asyncio.TimeoutError:
- # await ctx.send("You didn't respond in time, please redo this command.")
- # return
-
+ # await ctx.send("You didn't respond in time, please redo this command.")
+ # return
+ #
# try:
- # await message.add_reaction(actual_emoji)
+ # await message.add_reaction(actual_emoji)
# except discord.HTTPException:
- # await ctx.send("I can't add that emoji because I'm not in the guild that"
- # " owns it.")
- # return
-
+ # await ctx.send("I can't add that emoji because I'm not in the guild that"
+ # " owns it.")
+ # return
+ #
# noinspection PyTypeChecker
await self.add_reactrestrict(message_id, role)
@@ -258,10 +248,10 @@ class ReactRestrict:
Removes role associated with a given reaction.
"""
# try:
- # emoji, actual_emoji = await self._wait_for_emoji(ctx)
+ # emoji, actual_emoji = await self._wait_for_emoji(ctx)
# except asyncio.TimeoutError:
- # await ctx.send("You didn't respond in time, please redo this command.")
- # return
+ # await ctx.send("You didn't respond in time, please redo this command.")
+ # return
# noinspection PyTypeChecker
await self.remove_react(message_id, role)
@@ -305,50 +295,50 @@ class ReactRestrict:
for apprrole in roles:
if apprrole in member.roles:
return
-
+
message = await self._get_message_from_channel(channel_id, message_id)
await message.remove_reaction(emoji, member)
-
- # try:
- # await member.add_roles(*roles)
- # except discord.Forbidden:
- # pass
+ # try:
+ # await member.add_roles(*roles)
+ # except discord.Forbidden:
+ # pass
+ #
# async def on_raw_reaction_remove(self, emoji: discord.PartialReactionEmoji,
- # message_id: int, channel_id: int, user_id: int):
- # """
- # Event handler for long term reaction watching.
-
- # :param discord.PartialReactionEmoji emoji:
- # :param int message_id:
- # :param int channel_id:
- # :param int user_id:
- # :return:
- # """
- # if emoji.is_custom_emoji():
- # emoji_id = emoji.id
- # else:
- # emoji_id = emoji.name
-
- # has_reactrestrict, combos = await self.has_reactrestrict_combo(message_id, emoji_id)
-
- # if not has_reactrestrict:
- # return
-
- # try:
- # member = self._get_member(channel_id, user_id)
- # except LookupError:
- # return
-
- # if member.bot:
- # return
-
- # try:
- # roles = [self._get_role(member.guild, c.role_id) for c in combos]
- # except LookupError:
- # return
-
- # try:
- # await member.remove_roles(*roles)
- # except discord.Forbidden:
- # pass
+ # message_id: int, channel_id: int, user_id: int):
+ # """
+ # Event handler for long term reaction watching.
+ #
+ # :param discord.PartialReactionEmoji emoji:
+ # :param int message_id:
+ # :param int channel_id:
+ # :param int user_id:
+ # :return:
+ # """
+ # if emoji.is_custom_emoji():
+ # emoji_id = emoji.id
+ # else:
+ # emoji_id = emoji.name
+ #
+ # has_reactrestrict, combos = await self.has_reactrestrict_combo(message_id, emoji_id)
+ #
+ # if not has_reactrestrict:
+ # return
+ #
+ # try:
+ # member = self._get_member(channel_id, user_id)
+ # except LookupError:
+ # return
+ #
+ # if member.bot:
+ # return
+ #
+ # try:
+ # roles = [self._get_role(member.guild, c.role_id) for c in combos]
+ # except LookupError:
+ # return
+ #
+ # try:
+ # await member.remove_roles(*roles)
+ # except discord.Forbidden:
+ # pass
diff --git a/recyclingplant/__init__.py b/recyclingplant/__init__.py
new file mode 100644
index 0000000..e012d18
--- /dev/null
+++ b/recyclingplant/__init__.py
@@ -0,0 +1,9 @@
+from redbot.core import data_manager
+
+from .recyclingplant import RecyclingPlant
+
+
+def setup(bot):
+ plant = RecyclingPlant(bot)
+ data_manager.load_bundled_data(plant, __file__)
+ bot.add_cog(plant)
diff --git a/recyclingplant/data/junk.json b/recyclingplant/data/junk.json
new file mode 100644
index 0000000..c77f881
--- /dev/null
+++ b/recyclingplant/data/junk.json
@@ -0,0 +1,204 @@
+{
+ "can": [
+ {
+ "object": "Apple Core",
+ "action": "trash"
+ },
+ {
+ "object": "Paper Cup",
+ "action": "recycle"
+ },
+ {
+ "object": "Banana Peel",
+ "action": "trash"
+ },
+ {
+ "object": "Paper Bag",
+ "action": "recycle"
+ },
+ {
+ "object": "Old Taco",
+ "action": "trash"
+ },
+ {
+ "object": "Newspaper",
+ "action": "recycle"
+ },
+ {
+ "object": "Chewed Gum",
+ "action": "trash"
+ },
+ {
+ "object": "Polythene Bag",
+ "action": "recycle"
+ },
+ {
+ "object": "Rotten Eggs",
+ "action": "trash"
+ },
+ {
+ "object": "Outdated Telephone Directory",
+ "action": "recycle"
+ },
+ {
+ "object": "Stale Bread",
+ "action": "trash"
+ },
+ {
+ "object": "Used Notebook",
+ "action": "recycle"
+ },
+ {
+ "object": "Sour Milk",
+ "action": "trash"
+ },
+ {
+ "object": "Old Textbook",
+ "action": "recycle"
+ },
+ {
+ "object": "Week-Old Sandwich",
+ "action": "trash"
+ },
+ {
+ "object": "Paper Ball",
+ "action": "recycle"
+ },
+ {
+ "object": "Leftovers",
+ "action": "trash"
+ },
+ {
+ "object": "Toy Car",
+ "action": "recycle"
+ },
+ {
+ "object": "Fish Bones",
+ "action": "trash"
+ },
+ {
+ "object": "Superhero Costume",
+ "action": "recycle"
+ },
+ {
+ "object": "Dirty Diaper",
+ "action": "trash"
+ },
+ {
+ "object": "Silcone Mould",
+ "action": "recycle"
+ },
+ {
+ "object": "Mouldy Broccoli",
+ "action": "trash"
+ },
+ {
+ "object": "TV Remote",
+ "action": "recycle"
+ },
+ {
+ "object": "Withered Rose Bouquet",
+ "action": "trash"
+ },
+ {
+ "object": "Paper Plate",
+ "action": "recycle"
+ },
+ {
+ "object": "Slimy Bacon",
+ "action": "trash"
+ },
+ {
+ "object": "Folders",
+ "action": "recycle"
+ },
+ {
+ "object": "Fly Agaric Mushrooms",
+ "action": "trash"
+ },
+ {
+ "object": "Phone case",
+ "action": "recycle"
+ },
+ {
+ "object": "Napkins",
+ "action": "trash"
+ },
+ {
+ "object": "Broken Dualshock 4 Controller",
+ "action": "recycle"
+ },
+ {
+ "object": "Wax Paper",
+ "action": "trash"
+ },
+ {
+ "object": "iPad",
+ "action": "recycle"
+ },
+ {
+ "object": "Paint Can",
+ "action": "trash"
+ },
+ {
+ "object": "Glass Bottle",
+ "action": "recycle"
+ },
+ {
+ "object": "Light Bulb",
+ "action": "trash"
+ },
+ {
+ "object": "Nintendo 3DS",
+ "action": "recycle"
+ },
+ {
+ "object": "Styrofoam Container",
+ "action": "trash"
+ },
+ {
+ "object": "Flash Cards",
+ "action": "recycle"
+ },
+ {
+ "object": "Motor Oil Can",
+ "action": "trash"
+ },
+ {
+ "object": "Candy Wrapper",
+ "action": "recycle"
+ },
+ {
+ "object": "Waxed Cardboard",
+ "action": "trash"
+ },
+ {
+ "object": "Empty Bottle",
+ "action": "recycle"
+ },
+ {
+ "object": "Used Toilet Paper",
+ "action": "trash"
+ },
+ {
+ "object": "Outdated Calendar",
+ "action": "recycle"
+ },
+ {
+ "object": "Ceramic Mug",
+ "action": "trash"
+ },
+ {
+ "object": "Plastic Cup",
+ "action": "recycle"
+ },
+ {
+ "object": "Gift Wrapping",
+ "action": "trash"
+ },
+ {
+ "object": "Soda Bottle",
+ "action": "recycle"
+ }
+ ]
+}
diff --git a/recyclingplant/info.json b/recyclingplant/info.json
new file mode 100644
index 0000000..cab34d2
--- /dev/null
+++ b/recyclingplant/info.json
@@ -0,0 +1,21 @@
+{
+ "author": [
+ "Bobloy",
+ "SnappyDragon"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Apply for a job at the recycling plant! Sort out the garbage!",
+ "hidden": false,
+ "install_msg": "Thank you for installing RecyclingPlant. Start recycling today with `[p]load recyclingplant`, then `[p]recyclingplant`",
+ "requirements": [],
+ "short": "Apply for a job at the recycling plant!",
+ "tags": [
+ "bobloy",
+ "environment",
+ "games"
+ ]
+}
\ No newline at end of file
diff --git a/recyclingplant/recyclingplant.py b/recyclingplant/recyclingplant.py
new file mode 100644
index 0000000..171e5de
--- /dev/null
+++ b/recyclingplant/recyclingplant.py
@@ -0,0 +1,70 @@
+import asyncio
+import json
+import random
+
+from redbot.core import bank
+from redbot.core import commands
+from redbot.core.data_manager import cog_data_path
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class RecyclingPlant(Cog):
+ """Apply for a job at the recycling plant!"""
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.path = str(cog_data_path(self)).replace('\\', '/')
+ self.junk_path = self.path + "/bundled_data/junk.json"
+
+ with open(self.junk_path) as json_data:
+ self.junk = json.load(json_data)
+
+ @commands.command(aliases=["recycle"])
+ async def recyclingplant(self, ctx: commands.Context):
+ """Apply for a job at the recycling plant!"""
+ x = 0
+ reward = 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))
+ while x in range(0, 10):
+ used = random.choice(self.junk['can'])
+ if used['action'] == 'trash':
+ opp = 'recycle'
+ else:
+ opp = 'trash'
+ await ctx.send('``{}``! Will {} ``trash`` it or ``recycle`` it?'.format(used['object'],
+ ctx.author.display_name))
+
+ def check(m):
+ return m.author == ctx.author and m.channel == ctx.channel
+
+ try:
+ answer = await self.bot.wait_for('message', timeout=120, check=check)
+ except asyncio.TimeoutError:
+ answer = None
+
+ if answer is None:
+ await ctx.send('``{}`` fell down the conveyor belt to be sorted again!'.format(used['object']))
+ elif answer.content.lower().strip() == used['action']:
+ await ctx.send(
+ 'Congratulations! You put ``{}`` down the correct chute! (**+50**)'.format(used['object']))
+ reward = reward + 50
+ x += 1
+ elif answer.content.lower().strip() == opp:
+ await ctx.send('{}, you little brute, you put it down the wrong chute! (**-50**)'.format(
+ ctx.author.display_name))
+ reward = reward - 50
+ elif answer.content.lower().strip() == 'exit':
+ await ctx.send('{} has been relived of their duty.'.format(ctx.author.display_name))
+ break
+ else:
+ await ctx.send('``{}`` fell down the conveyor belt to be sorted again!'.format(used['object']))
+ else:
+ if reward > 0:
+ bank.deposit_credits(ctx.author, reward)
+ await ctx.send(
+ '{} been given **{} {}s** for your services.'.format(ctx.author.display_name, reward,
+ bank.get_currency_name(ctx.guild)))
diff --git a/rpsls/__init__.py b/rpsls/__init__.py
new file mode 100644
index 0000000..46a1600
--- /dev/null
+++ b/rpsls/__init__.py
@@ -0,0 +1,5 @@
+from .rpsls import RPSLS
+
+
+def setup(bot):
+ bot.add_cog(RPSLS(bot))
diff --git a/rpsls/info.json b/rpsls/info.json
new file mode 100644
index 0000000..f1ac3b6
--- /dev/null
+++ b/rpsls/info.json
@@ -0,0 +1,21 @@
+{
+ "author": [
+ "Bobloy",
+ "SnappyDragon"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Play Rock Papers Scissor Lizard Spock by Sam Kass in Discord!",
+ "hidden": false,
+ "install_msg": "Thank you for installing RPSLP. Get started with `[p]load rpsls`, then `[p]rpsls`",
+ "requirements": [],
+ "short": "Play Rock Papers Scissor Lizard Spock in Discord!",
+ "tags": [
+ "bobloy",
+ "star trek",
+ "games"
+ ]
+}
\ No newline at end of file
diff --git a/rpsls/rpsls.py b/rpsls/rpsls.py
new file mode 100644
index 0000000..7152cad
--- /dev/null
+++ b/rpsls/rpsls.py
@@ -0,0 +1,93 @@
+import asyncio
+import random
+
+import discord
+from redbot.core import commands
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class RPSLS(Cog):
+ """Play Rock Paper Scissors Lizard Spock."""
+
+ weaknesses = {
+ "rock": [
+ "paper",
+ "spock"
+ ],
+ "paper": [
+ "scissors",
+ "lizard"
+ ],
+ "scissors": [
+ "spock",
+ "rock"
+ ],
+ "lizard": [
+ "scissors",
+ "rock"
+ ],
+ "spock": [
+ "paper",
+ "lizard"
+ ]
+ }
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command()
+ async def rpsls(self, ctx: commands.Context, choice: str):
+ """
+ Play Rock Paper Scissors Lizard Spock by Sam Kass in Discord!
+
+ Rules:
+ Scissors cuts Paper
+ Paper covers Rock
+ Rock crushes Lizard
+ Lizard poisons Spock
+ Spock smashes Scissors
+ Scissors decapitates Lizard
+ Lizard eats Paper
+ Paper disproves Spock
+ Spock vaporizes Rock
+ And as it has always Rock crushes Scissors
+ """
+
+ player_choice = choice.lower()
+ player_emote = self.get_emote(player_choice)
+ if player_emote is None:
+ await ctx.maybe_send_embed("Invalid Choice")
+ return
+
+ bot_choice = random.choice(list(self.weaknesses.keys()))
+ bot_emote = self.get_emote(bot_choice)
+ message = '{} vs. {}, who will win?'.format(player_emote, bot_emote)
+ em = discord.Embed(description=message, color=discord.Color.blue())
+ await ctx.send(embed=em)
+ await asyncio.sleep(2)
+ if player_choice in self.weaknesses[bot_choice]:
+ message = 'You win! :sob:'
+ em_color = discord.Color.green()
+ elif bot_choice in self.weaknesses[player_choice]:
+ message = 'I win! :smile:'
+ em_color = discord.Color.red()
+ else:
+ message = 'It\'s a draw! :neutral_face:'
+ em_color = discord.Color.blue()
+ em = discord.Embed(description=message, color=em_color)
+ await ctx.send(embed=em)
+
+ def get_emote(self, choice):
+ if choice == 'rock':
+ emote = ':moyai:'
+ elif choice == 'spock':
+ emote = ':vulcan:'
+ elif choice == 'paper':
+ emote = ':page_facing_up:'
+ elif choice in ['scissors', 'lizard']:
+ emote = ':{}:'.format(choice)
+ else:
+ emote = None
+ return emote
diff --git a/sayurl/__init__.py b/sayurl/__init__.py
new file mode 100644
index 0000000..f4440e1
--- /dev/null
+++ b/sayurl/__init__.py
@@ -0,0 +1,5 @@
+from .sayurl import SayUrl
+
+
+def setup(bot):
+ bot.add_cog(SayUrl(bot))
diff --git a/sayurl/info..json b/sayurl/info..json
new file mode 100644
index 0000000..1c44fb1
--- /dev/null
+++ b/sayurl/info..json
@@ -0,0 +1,19 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Convert any website into text and post it in chat",
+ "hidden": true,
+ "install_msg": "Thank you for installing SayUrl! Get started with `[p]load forcemention`, then `[p]help SayUrl",
+ "requirements": ["html2text"],
+ "short": "Convert URL to text",
+ "tags": [
+ "bobloy",
+ "tools"
+ ]
+}
\ No newline at end of file
diff --git a/sayurl/sayurl.py b/sayurl/sayurl.py
new file mode 100644
index 0000000..d56720e
--- /dev/null
+++ b/sayurl/sayurl.py
@@ -0,0 +1,57 @@
+import aiohttp
+import html2text
+
+from redbot.core import Config, commands
+from redbot.core.bot import Red
+from redbot.core.utils.chat_formatting import pagify
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+async def fetch_url(session, url):
+ async with session.get(url) as response:
+ assert response.status == 200
+ return await response.text()
+
+
+class SayUrl(Cog):
+ """
+ V3 Cog Template
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {}
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @commands.command()
+ async def sayurl(self, ctx: commands.Context, url):
+ """
+ Converts a URL to something readable
+
+ Works better on smaller websites
+ """
+
+ h = html2text.HTML2Text()
+ h.ignore_links = True
+ # h.ignore_images = True
+ h.images_to_alt = True
+
+ h.escape_snob = True
+ h.skip_internal_links = True
+ h.ignore_tables = True
+ h.single_line_break = True
+ h.mark_code = True
+ h.wrap_links = True
+ h.ul_item_mark = '-'
+
+ async with aiohttp.ClientSession() as session:
+ site = await fetch_url(session, url)
+
+ for page in pagify(h.handle(site)):
+ await ctx.send(page)
diff --git a/scp/__init__.py b/scp/__init__.py
new file mode 100644
index 0000000..bded2a5
--- /dev/null
+++ b/scp/__init__.py
@@ -0,0 +1,5 @@
+from .scp import SCP
+
+
+def setup(bot):
+ bot.add_cog(SCP(bot))
diff --git a/scp/info.json b/scp/info.json
new file mode 100644
index 0000000..4ac9ea9
--- /dev/null
+++ b/scp/info.json
@@ -0,0 +1,20 @@
+{
+ "author": [
+ "Bobloy",
+ "SnappyDragon"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Look up SCP articles. Warning: Some of them may be too creepy or gruesome.",
+ "hidden": false,
+ "install_msg": "You are now connected to the SCP database. You may now proceed to access the data using `[p]load scp`, then `[p]help SCP`",
+ "requirements": [],
+ "short": "Look up SCP articles.",
+ "tags": [
+ "bobloy",
+ "gruesom"
+ ]
+}
\ No newline at end of file
diff --git a/scp/scp.py b/scp/scp.py
new file mode 100644
index 0000000..1457ca1
--- /dev/null
+++ b/scp/scp.py
@@ -0,0 +1,119 @@
+import discord
+from redbot.core import commands
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class SCP(Cog):
+ """Look up SCP articles. Warning: Some of them may be too creepy or gruesome."""
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.command()
+ async def scp(self, ctx: commands.Context, num: int):
+ """Look up SCP articles.
+
+ Warning: Some of them may be too creepy or gruesome.
+ Reminder: You must specify a number between 1 and 4999.
+ """
+
+ # Thanks Shigbeard and Redjumpman for helping me!
+
+ if 0 < num <= 4999:
+ msg = "http://www.scp-wiki.net/scp-{:03}".format(num)
+ c = discord.Color.green()
+ else:
+ msg = "You must specify a number between 1 and 4999."
+ c = discord.Color.red()
+
+ if ctx.embed_requested():
+ await ctx.send(embed=discord.Embed(description=msg, color=c))
+ else:
+ await ctx.maybe_send_embed(msg)
+
+ @commands.command()
+ async def scpj(self, ctx: commands.Context, joke: str):
+ """Look up SCP-Js.
+
+ Reminder: Enter the correct name or else the resultant page will be invalid.
+ Use 001, etc. in case of numbers less than 100.
+ """
+
+ msg = "http://www.scp-wiki.net/scp-{}-j".format(joke)
+ await ctx.maybe_send_embed(msg)
+
+ @commands.command()
+ async def scparc(self, ctx: commands.Context, num: int):
+ """Look up SCP archives.
+
+ Warning: Some of them may be too creepy or gruesome."""
+ valid_archive = (
+ 13, 48, 51, 89, 91, 112, 132, 138, 157, 186, 232, 234,
+ 244, 252, 257, 338, 356, 361, 400, 406, 503, 515, 517,
+ 578, 728, 744, 776, 784, 837, 922, 987, 1023)
+ if num in valid_archive:
+ msg = "http://www.scp-wiki.net/scp-{:03}-arc".format(num)
+ c = discord.Color.green()
+ em = discord.Embed(description=msg, color=c)
+ else:
+ ttl = "You must specify a valid archive number."
+ msg = "{}".format(valid_archive)
+ c = discord.Color.red()
+
+ em = discord.Embed(title=ttl, description=msg, color=c)
+
+ if ctx.embed_requested():
+ await ctx.send(embed=em)
+ else:
+ await ctx.maybe_send_embed(msg)
+
+ @commands.command()
+ async def scpex(self, ctx: commands.Context, num: int):
+ """Look up explained SCP articles.
+
+ Warning: Some of them may be too creepy or gruesome.
+ """
+
+ valid_archive = (711, 920, 1841, 1851, 1974, 2600, 4023, 8900)
+ if num in valid_archive:
+ msg = "http://www.scp-wiki.net/scp-{:03}-ex".format(num)
+ c = discord.Color.green()
+ em = discord.Embed(description=msg, color=c)
+ else:
+ ttl = "You must specify a valid archive number."
+ msg = "{}".format(valid_archive)
+ c = discord.Color.red()
+
+ em = discord.Embed(title=ttl, description=msg, color=c)
+
+ if ctx.embed_requested():
+ await ctx.send(embed=em)
+ else:
+ await ctx.maybe_send_embed(msg)
+
+ @commands.command()
+ async def anomalousitems(self, ctx: commands.Context):
+ """Look through the log of anomalous items."""
+
+ msg = "http://www.scp-wiki.net/log-of-anomalous-items"
+ await ctx.maybe_send_embed(msg)
+
+ @commands.command()
+ async def extranormalevents(self, ctx: commands.Context):
+ """Look through the log of extranormal events."""
+
+ msg = "http://www.scp-wiki.net/log-of-extranormal-events"
+ await ctx.maybe_send_embed(msg)
+
+ @commands.command()
+ async def unexplainedlocations(self, ctx: commands.Context):
+ """Look through the log of unexplained locations."""
+
+ msg = "http://www.scp-wiki.net/log-of-unexplained-locations"
+ await ctx.maybe_send_embed(msg)
+
+
+def setup(bot):
+ bot.add_cog(SCP(bot))
diff --git a/secrethitler/__init__.py b/secrethitler/__init__.py
index 9430e4a..c97eef1 100644
--- a/secrethitler/__init__.py
+++ b/secrethitler/__init__.py
@@ -1,4 +1,4 @@
-from .werewolf import Werewolf
+from .secrethitler import Werewolf
def setup(bot):
diff --git a/secrethitler/secrethitler.py b/secrethitler/secrethitler.py
index 9e98c09..2f9360b 100644
--- a/secrethitler/secrethitler.py
+++ b/secrethitler/secrethitler.py
@@ -1,9 +1,9 @@
import asyncio
import discord
-from discord.ext import commands
-from redbot.core import Config
+
+from redbot.core import Config, commands
from datetime import datetime, timedelta
@@ -33,7 +33,7 @@ class Werewolf:
Base command for this cog. Check help for the commands list.
"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
+ pass
@ww.command()
async def new(self, ctx, game_code):
diff --git a/stealemoji/info..json b/stealemoji/info..json
new file mode 100644
index 0000000..67d2ad9
--- /dev/null
+++ b/stealemoji/info..json
@@ -0,0 +1,20 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Steals custom emojis the bot sees and moves them to an emoji server",
+ "hidden": true,
+ "install_msg": "Thank you for installing StealEmoji",
+ "requirements": [],
+ "short": "Steals custom emojis",
+ "tags": [
+ "bobloy",
+ "utils",
+ "emoji"
+ ]
+}
\ No newline at end of file
diff --git a/stealemoji/stealemoji.py b/stealemoji/stealemoji.py
index 670fa61..155d596 100644
--- a/stealemoji/stealemoji.py
+++ b/stealemoji/stealemoji.py
@@ -1,18 +1,13 @@
-import asyncio
import aiohttp
-from urllib.parse import urlparse
-
-import os
-
-from typing import List, Union
-
import discord
-from discord.ext import commands
-from redbot.core import Config
+from redbot.core import Config, commands
from redbot.core.bot import Red
-# from redbot.core import data_manager
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
async def fetch_img(session, url):
with aiohttp.Timeout(10):
@@ -20,31 +15,31 @@ async def fetch_img(session, url):
assert response.status == 200
return await response.read()
-
-class StealEmoji:
+
+class StealEmoji(Cog):
"""
This cog steals emojis and creates servers for them
"""
+ default_stolemoji = {
+ "guildbank": None,
+ "name": None,
+ "require_colons": False,
+ "managed": False,
+ "guild_id": None,
+ "url": None,
+ "animated": False
+ }
+
def __init__(self, red: Red):
self.bot = red
self.config = Config.get_conf(self, identifier=11511610197108101109111106105)
- default_global = {
- "stolemoji": {},
- "guildbanks": [],
- "on": False
- }
-
- default_stolemoji = {
- "guildbank": None,
- "name": None,
- "require_colons": False,
- "managed": False,
- "guild_id": None,
- "url": None,
- "animated": False
- }
-
+ default_global = {
+ "stolemoji": {},
+ "guildbanks": [],
+ "on": False
+ }
+
self.config.register_global(**default_global)
@commands.group()
@@ -53,114 +48,111 @@ class StealEmoji:
Base command for this cog. Check help for the commands list.
"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
+ pass
@stealemoji.command(name="collect")
async def se_collect(self, ctx):
"""Toggles whether emoji's are collected or not"""
- currSetting = await self.config.on()
- await self.config.on.set(not currSetting)
- await ctx.send("Collection is now "+str(not currSetting))
-
- @stealemoji.command(name="bank")
+ curr_setting = await self.config.on()
+ await self.config.on.set(not curr_setting)
+ await ctx.send("Collection is now " + str(not curr_setting))
+
+ @stealemoji.command(name="bank")
async def se_bank(self, ctx):
"""Add current server as emoji bank"""
await ctx.send("This will upload custom emojis to this server\n"
- "Are you sure you want to make the current server an emoji bank? (y/n)")
-
+ "Are you sure you want to make the current server an emoji bank? (y//n)")
+
def check(m):
- return m.content.upper() in ["Y","YES","N","NO"] and m.channel == ctx.channel and m.author == ctx.author
+ return m.content.upper() in ["Y", "YES", "N", "NO"] and m.channel == ctx.channel and m.author == ctx.author
msg = await self.bot.wait_for('message', check=check)
-
- if msg.content in ["N","NO"]:
+
+ if msg.content in ["N", "NO"]:
await ctx.send("Cancelled")
return
-
+
async with self.config.guildbanks() as guildbanks:
guildbanks.append(ctx.guild.id)
-
+
await ctx.send("This server has been added as an emoji bank")
-
+
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User):
"""Event handler for reaction watching"""
if not reaction.custom_emoji:
print("Not a custom emoji")
return
-
+
if not (await self.config.on()):
print("Collecting is off")
return
-
+
emoji = reaction.emoji
if emoji in self.bot.emojis:
print("Emoji already in bot.emojis")
return
-
+
# This is now a custom emoji that the bot doesn't have access to, time to steal it
# First, do I have an available guildbank?
-
-
+
guildbank = None
banklist = await self.config.guildbanks()
for guild_id in banklist:
guild = self.bot.get_guild(guild_id)
- if len(guild.emojis)<50:
+ if len(guild.emojis) < 50:
guildbank = guild
break
-
+
if guildbank is None:
print("No guildbank to store emoji")
# Eventually make a new banklist
return
-
+
# Next, have I saved this emoji before (because uploaded emoji != orignal emoji)
-
+
stolemojis = await self.config.stolemoji()
-
+
if emoji.id in stolemojis:
print("Emoji has already been stolen")
return
-
+
# Alright, time to steal it for real
# path = urlparse(emoji.url).path
# ext = os.path.splitext(path)[1]
-
+
async with aiohttp.ClientSession() as session:
img = await fetch_img(session, emoji.url)
-
+
# path = data_manager.cog_data_path(cog_instance=self) / (emoji.name+ext)
-
+
# with path.open("wb") as f:
- # f.write(img)
+ # f.write(img)
# urllib.urlretrieve(emoji.url, emoji.name+ext)
-
-
+
try:
- await guildbank.create_custom_emoji(name=emoji.name,image=img,reason="Stole from "+str(user))
- except Forbidden as e:
+ await guildbank.create_custom_emoji(name=emoji.name, image=img, reason="Stole from " + str(user))
+ except discord.Forbidden as e:
print("PermissionError - no permission to add emojis")
raise PermissionError("No permission to add emojis") from e
- except HTTPException:
+ except discord.HTTPException as e:
print("HTTPException exception")
- raise HTTPException # Unhandled error
+ raise e # Unhandled error
# If you get this far, YOU DID IT
-
+
save_dict = self.default_stolemoji.copy()
e_dict = vars(emoji)
-
+
for k in e_dict:
if k in save_dict:
save_dict[k] = e_dict[k]
-
+
save_dict["guildbank"] = guildbank.id
-
+
async with self.config.stolemoji() as stolemoji:
stolemoji[emoji.id] = save_dict
-
- #Enable the below if you want to get notified when it works
+
+ # Enable the below if you want to get notified when it works
# owner = await self.bot.application_info()
# owner = owner.owner
# await owner.send("Just added emoji "+str(emoji)+" to server "+str(guildbank))
-
diff --git a/timerole/__init__.py b/timerole/__init__.py
new file mode 100644
index 0000000..9e7f94b
--- /dev/null
+++ b/timerole/__init__.py
@@ -0,0 +1,5 @@
+from .timerole import Timerole
+
+
+def setup(bot):
+ bot.add_cog(Timerole(bot))
diff --git a/timerole/info.json b/timerole/info.json
new file mode 100644
index 0000000..7ce0c5c
--- /dev/null
+++ b/timerole/info.json
@@ -0,0 +1,22 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Apply roles based on the # of days on server",
+ "hidden": false,
+ "install_msg": "Thank you for installing timerole.\nGet started with `[p]load timerole`. Configure with [p]timerole",
+ "requirements": [],
+ "short": "Apply roles after # of days",
+ "tags": [
+ "bobloy",
+ "utilities",
+ "tools",
+ "tool",
+ "roles"
+ ]
+}
\ No newline at end of file
diff --git a/timerole/timerole.py b/timerole/timerole.py
new file mode 100644
index 0000000..9582bf6
--- /dev/null
+++ b/timerole/timerole.py
@@ -0,0 +1,146 @@
+import asyncio
+from datetime import timedelta, datetime
+
+import discord
+from redbot.core import Config, checks, commands
+from redbot.core.bot import Red
+from redbot.core.utils.chat_formatting import pagify
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class Timerole(Cog):
+ """Add roles to users based on time on server"""
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {
+ 'announce': None,
+ 'roles': {}
+ }
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @commands.command()
+ @checks.guildowner()
+ @commands.guild_only()
+ async def runtimerole(self, ctx: commands.Context):
+ """Trigger the daily timerole"""
+
+ await self.timerole_update()
+ await ctx.send("Success")
+
+ @commands.group()
+ @checks.mod_or_permissions(administrator=True)
+ @commands.guild_only()
+ async def timerole(self, ctx):
+ """Adjust timerole settings"""
+ if ctx.invoked_subcommand is None:
+ pass
+
+ @timerole.command()
+ async def addrole(self, ctx: commands.Context, role: discord.Role, days: int, *requiredroles: discord.Role):
+ """Add a role to be added after specified time on server"""
+ guild = ctx.guild
+
+ to_set = {'days': days}
+ if requiredroles:
+ to_set['required'] = [r.id for r in requiredroles]
+
+ await self.config.guild(guild).roles.set_raw(role.id, value=to_set)
+ await ctx.send("Time Role for {0} set to {1} days".format(role.name, days))
+
+ @timerole.command()
+ async def channel(self, ctx: commands.Context, channel: discord.TextChannel):
+ """Sets the announce channel for role adds"""
+ guild = ctx.guild
+
+ await self.config.guild(guild).announce.set(channel.id)
+ await ctx.send("Announce channel set to {0}".format(channel.mention))
+
+ @timerole.command()
+ async def removerole(self, ctx: commands.Context, role: discord.Role):
+ """Removes a role from being added after specified time"""
+ guild = ctx.guild
+
+ await self.config.guild(guild).roles.set_raw(role.id, value=None)
+ await ctx.send("{0} will no longer be applied".format(role.name))
+
+ @timerole.command()
+ async def list(self, ctx: commands.Context):
+ """Lists all currently setup timeroles"""
+ guild = ctx.guild
+
+ role_dict = await self.config.guild(guild).roles()
+ out = ""
+ for r_id, r_data in role_dict.items():
+ if r_data is not None:
+ role = discord.utils.get(guild.roles, id=int(r_id))
+ r_roles = []
+ if role is None:
+ role = r_id
+ if 'required' in r_data:
+ r_roles = [str(discord.utils.get(guild.roles, id=int(new_id))) for new_id in r_data['required']]
+ out += "{} || {} days || requires: {}\n".format(str(role), r_data['days'], r_roles)
+ await ctx.maybe_send_embed(out)
+
+ async def timerole_update(self):
+ for guild in self.bot.guilds:
+ addlist = []
+
+ role_dict = await self.config.guild(guild).roles()
+ if not any(role_data for role_data in role_dict.values()): # No roles
+ continue
+
+ for member in guild.members:
+ has_roles = [r.id for r in member.roles]
+
+ get_roles = [int(rID) for rID, r_data in role_dict.items() if r_data is not None]
+
+ check_roles = set(get_roles) - set(has_roles)
+
+ for role_id in check_roles:
+ # Check for required role
+ if 'required' in role_dict[str(role_id)]:
+ if not set(role_dict[str(role_id)]['required']) & set(has_roles):
+ # Doesn't have required role
+ continue
+
+ if member.joined_at + timedelta(
+ days=role_dict[str(role_id)]['days']) <= datetime.today():
+ # Qualifies
+ addlist.append((member, role_id))
+
+ channel = await self.config.guild(guild).announce()
+ if channel is not None:
+ channel = guild.get_channel(channel)
+
+ title = "**These members have received the following roles**\n"
+ results = ""
+ for member, role_id in addlist:
+ role = discord.utils.get(guild.roles, id=role_id)
+ await member.add_roles(role, reason="Timerole")
+ results += "{} : {}\n".format(member.display_name, role.name)
+
+ if channel is not None and results:
+ await channel.send(title)
+ for page in pagify(
+ results, shorten_by=50):
+ await channel.send(page)
+
+ async def check_day(self):
+ while self is self.bot.get_cog("Timerole"):
+ tomorrow = datetime.now() + timedelta(days=1)
+ midnight = datetime(year=tomorrow.year, month=tomorrow.month,
+ day=tomorrow.day, hour=0, minute=0, second=0)
+
+ await asyncio.sleep((midnight - datetime.now()).seconds)
+
+ await self.timerole_update()
+
+ await asyncio.sleep(3)
+ # then start loop over again
diff --git a/tts/__init__.py b/tts/__init__.py
new file mode 100644
index 0000000..47959b8
--- /dev/null
+++ b/tts/__init__.py
@@ -0,0 +1,5 @@
+from .tts import TTS
+
+
+def setup(bot):
+ bot.add_cog(TTS(bot))
diff --git a/tts/info..json b/tts/info..json
new file mode 100644
index 0000000..6810a42
--- /dev/null
+++ b/tts/info..json
@@ -0,0 +1,22 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Send Text2Speech messages as an uploaded mp3",
+ "hidden": true,
+ "install_msg": "Thank you for installing TTS. Get started with `[p]load tts`, then `[p]help TTS`",
+ "requirements": [
+ "gTTS"
+ ],
+ "short": "Send TTS messages as uploaded mp3",
+ "tags": [
+ "bobloy",
+ "utils",
+ "audio"
+ ]
+}
\ No newline at end of file
diff --git a/tts/tts.py b/tts/tts.py
new file mode 100644
index 0000000..7288a9c
--- /dev/null
+++ b/tts/tts.py
@@ -0,0 +1,35 @@
+import io
+
+import discord
+from gtts import gTTS
+from redbot.core import Config, commands
+from redbot.core.bot import Red
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class TTS(Cog):
+ """
+ Send Text-to-Speech messages
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {}
+ default_guild = {}
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ @commands.command(aliases=["t2s", "text2"])
+ async def tts(self, ctx: commands.Context, *, text: str):
+ """
+ Send Text to speech messages as an mp3
+ """
+ mp3_fp = io.BytesIO()
+ tts = gTTS(text, 'en')
+ tts.write_to_fp(mp3_fp)
+ await ctx.send(file=discord.File(mp3_fp.getvalue(), "text.mp3"))
diff --git a/unicode/__init__.py b/unicode/__init__.py
new file mode 100644
index 0000000..410d42c
--- /dev/null
+++ b/unicode/__init__.py
@@ -0,0 +1,5 @@
+from .unicode import Unicode
+
+
+def setup(bot):
+ bot.add_cog(Unicode(bot))
diff --git a/unicode/info.json b/unicode/info.json
new file mode 100644
index 0000000..0d8d24b
--- /dev/null
+++ b/unicode/info.json
@@ -0,0 +1,21 @@
+{
+ "author": [
+ "Bobloy",
+ "SnappyDragon"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Encode/Decode Unicode characters!",
+ "hidden": false,
+ "install_msg": "\u0048\u0065\u006c\u006c\u006f\u0021 \u0054\u0068\u0069\u0073 \u006d\u0065\u0073\u0073\u0061\u0067\u0065 \u0077\u0061\u0073 \u0077\u0072\u0069\u0074\u0074\u0065\u006e \u0069\u006e \u0055\u004e\u0049\u0043\u004f\u0044\u0045\u002e",
+ "requirements": [],
+ "short": "Encode/Decode Unicode characters!",
+ "tags": [
+ "bobloy",
+ "utility",
+ "tools"
+ ]
+}
\ No newline at end of file
diff --git a/unicode/unicode.py b/unicode/unicode.py
new file mode 100644
index 0000000..e305ad4
--- /dev/null
+++ b/unicode/unicode.py
@@ -0,0 +1,52 @@
+import codecs as c
+
+import discord
+from redbot.core import commands
+from typing import Any
+
+Cog: Any = getattr(commands, "Cog", object)
+
+
+class Unicode(Cog):
+ """Encode/Decode Unicode characters!"""
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ @commands.group(name='unicode', pass_context=True)
+ async def unicode(self, ctx):
+ """Encode/Decode a Unicode character."""
+ if ctx.invoked_subcommand is None:
+ pass
+
+ @unicode.command()
+ async def decode(self, ctx: commands.Context, character):
+ """Decode a Unicode character."""
+ try:
+ data = 'U+{:04X}'.format(ord(character[0]))
+ color = discord.Color.green()
+ except ValueError:
+ data = ''
+ color = discord.Color.red()
+ em = discord.Embed(title=character, description=data, color=color)
+ await ctx.send(embed=em)
+
+ @unicode.command()
+ async def encode(self, ctx:commands.Context, character):
+ """Encode an Unicode character."""
+ try:
+ if character[:2] == '\\u':
+ data = repr(c.decode(character, 'unicode-escape'))
+ data = data.strip("'")
+ color = discord.Color.green()
+ elif character[:2] == 'U+':
+ data = chr(int(character.lstrip('U+'), 16))
+ color = discord.Color.green()
+ else:
+ data = ''
+ color = discord.Color.red()
+ except ValueError:
+ data = ''
+ color = discord.Color.red()
+ em = discord.Embed(title=character, description=data, color=color)
+ await ctx.send(embed=em)
diff --git a/werewolf/builder.py b/werewolf/builder.py
index d14b25e..da0bf1e 100644
--- a/werewolf/builder.py
+++ b/werewolf/builder.py
@@ -1,21 +1,96 @@
+import bisect
+from collections import defaultdict
+from random import choice
+
import discord
+
+
# Import all roles here
+from redbot.core import commands
+
from werewolf.roles.seer import Seer
from werewolf.roles.vanillawerewolf import VanillaWerewolf
from werewolf.roles.villager import Villager
+from redbot.core.utils.menus import menu, prev_page, next_page, close_menu
# All roles in this list for iterating
-role_list = [Villager, VanillaWerewolf]
+
+ROLE_LIST = sorted([Villager, Seer, VanillaWerewolf], key=lambda x: x.alignment)
+
+ALIGNMENT_COLORS = [0x008000, 0xff0000, 0xc0c0c0]
+TOWN_ROLES = [(idx, role) for idx, role in enumerate(ROLE_LIST) if role.alignment == 1]
+WW_ROLES = [(idx, role) for idx, role in enumerate(ROLE_LIST) if role.alignment == 2]
+OTHER_ROLES = [(idx, role) for idx, role in enumerate(ROLE_LIST) if role.alignment not in [0, 1]]
+
+ROLE_PAGES = []
+PAGE_GROUPS = [0]
+
+ROLE_CATEGORIES = {
+ 1: "Random", 2: "Investigative", 3: "Protective", 4: "Government",
+ 5: "Killing", 6: "Power (Special night action)",
+ 11: "Random", 12: "Deception", 15: "Killing", 16: "Support",
+ 21: "Benign", 22: "Evil", 23: "Killing"}
+
+CATEGORY_COUNT = []
+
+
+def role_embed(idx, role, color):
+ embed = discord.Embed(title="**{}** - {}".format(idx, str(role.__name__)), description=role.game_start_message,
+ color=color)
+ embed.add_field(name='Alignment', value=['Town', 'Werewolf', 'Neutral'][role.alignment - 1], inline=True)
+ embed.add_field(name='Multiples Allowed', value=str(not role.unique), inline=True)
+ embed.add_field(name='Role Type', value=", ".join(ROLE_CATEGORIES[x] for x in role.category), inline=True)
+ embed.add_field(name='Random Option', value=str(role.rand_choice), inline=True)
+
+ return embed
+
+
+def setup():
+ # Roles
+ last_alignment = ROLE_LIST[0].alignment
+ for idx, role in enumerate(ROLE_LIST):
+ if role.alignment != last_alignment and len(ROLE_PAGES) - 1 not in PAGE_GROUPS:
+ PAGE_GROUPS.append(len(ROLE_PAGES) - 1)
+ last_alignment = role.alignment
+
+ ROLE_PAGES.append(role_embed(idx, role, ALIGNMENT_COLORS[role.alignment - 1]))
+
+ # Random Town Roles
+ if len(ROLE_PAGES) - 1 not in PAGE_GROUPS:
+ PAGE_GROUPS.append(len(ROLE_PAGES) - 1)
+ for k, v in ROLE_CATEGORIES.items():
+ if 0 < k <= 6:
+ ROLE_PAGES.append(discord.Embed(title="RANDOM:Town Role", description="Town {}".format(v), color=0x008000))
+ CATEGORY_COUNT.append(k)
+
+ # Random WW Roles
+ if len(ROLE_PAGES) - 1 not in PAGE_GROUPS:
+ PAGE_GROUPS.append(len(ROLE_PAGES) - 1)
+ for k, v in ROLE_CATEGORIES.items():
+ if 10 < k <= 16:
+ ROLE_PAGES.append(
+ discord.Embed(title="RANDOM:Werewolf Role", description="Werewolf {}".format(v), color=0xff0000))
+ CATEGORY_COUNT.append(k)
+ # Random Neutral Roles
+ if len(ROLE_PAGES) - 1 not in PAGE_GROUPS:
+ PAGE_GROUPS.append(len(ROLE_PAGES) - 1)
+ for k, v in ROLE_CATEGORIES.items():
+ if 20 < k <= 26:
+ ROLE_PAGES.append(
+ discord.Embed(title="RANDOM:Neutral Role", description="Neutral {}".format(v), color=0xc0c0c0))
+ CATEGORY_COUNT.append(k)
+
"""
Example code:
0 = Villager
1 = VanillaWerewolf
-E1 = Random Town
-R1 = Random Werewolf
-J1 = Benign Neutral
+T1 - T6 = Random Town (1: Random, 2: Investigative, 3: Protective, 4: Government,
+ 5: Killing, 6: Power (Special night action))
+W1, W2, W5, W6 = Random Werewolf
+N1 = Benign Neutral
-0001-1112E11R112P2
+0001-1112T11W112N2
0,0,0,1,11,12,E1,R1,R1,R1,R2,P2
pre-letter = exact role position
@@ -23,29 +98,229 @@ double digit position preempted by `-`
"""
-async def parse_code(code):
+async def parse_code(code, game):
"""Do the magic described above"""
- out = []
- decode = code.copy() # for now, pass exact names
- for role_id in decode:
- print(role_id)
- if role_id == "Villager":
- role = Villager
- elif role_id == "VanillaWerewolf":
- role = VanillaWerewolf
- elif role_id == "Seer":
- role = Seer
- else: # Fail to parse
- return None
- out.append(role)
-
- return out
-
-
-async def build_game(channel: discord.TextChannel):
- await channel.send("Not currently available")
-
- code = 12345678
-
- await channel.send("Your game code is **`{}`**".format(code))
- # Make this embeds
+ decode = []
+
+ digits = 1
+ built = ""
+ category = ""
+ for c in code:
+ if len(built) < digits:
+ built += c
+
+ if built == "T" or built == "W" or built == "N":
+ # Random Towns
+ category = built
+ built = ""
+ digits = 1
+ continue
+ elif built == "-":
+ built = ""
+ digits += 1
+ continue
+
+ try:
+ idx = int(built)
+ except ValueError:
+ raise ValueError("Invalid code")
+
+ if category == "": # no randomness yet
+ decode.append(ROLE_LIST[idx](game))
+ else:
+ options = []
+ if category == "T":
+ options = [role for role in ROLE_LIST if idx in role.category]
+ elif category == "W":
+ options = [role for role in ROLE_LIST if 10 + idx in role.category]
+ elif category == "N":
+ options = [role for role in ROLE_LIST if 20 + idx in role.category]
+ pass
+
+ if not options:
+ raise IndexError("No Match Found")
+
+ decode.append(choice(options)(game))
+
+ built = ""
+
+ return decode
+
+
+async def encode(roles, rand_roles):
+ """Convert role list to code"""
+ out_code = ""
+
+ digit_sort = sorted(role for role in roles if role < 10)
+ for role in digit_sort:
+ out_code += str(role)
+
+ digit_sort = sorted(role for role in roles if 10 <= role < 100)
+ if digit_sort:
+ out_code += "-"
+ for role in digit_sort:
+ out_code += str(role)
+ # That covers up to 99 roles, add another set here if we breach 100
+
+ if rand_roles:
+ # town sort
+ digit_sort = sorted(role for role in rand_roles if role <= 6)
+ if digit_sort:
+ out_code += "T"
+ for role in digit_sort:
+ out_code += str(role)
+
+ # werewolf sort
+ digit_sort = sorted(role for role in rand_roles if 10 < role <= 20)
+ if digit_sort:
+ out_code += "W"
+ for role in digit_sort:
+ out_code += str(role)
+
+ # neutral sort
+ digit_sort = sorted(role for role in rand_roles if 20 < role <= 30)
+ if digit_sort:
+ out_code += "N"
+ for role in digit_sort:
+ out_code += str(role)
+
+ return out_code
+
+
+async def next_group(ctx: commands.Context, pages: list,
+ controls: dict, message: discord.Message, page: int,
+ timeout: float, emoji: str):
+ perms = message.channel.permissions_for(ctx.guild.me)
+ if perms.manage_messages: # Can manage messages, so remove react
+ try:
+ await message.remove_reaction(emoji, ctx.author)
+ except discord.NotFound:
+ pass
+ page = bisect.bisect_right(PAGE_GROUPS, page)
+
+ if page == len(PAGE_GROUPS):
+ page = PAGE_GROUPS[0]
+ else:
+ page = PAGE_GROUPS[page]
+
+ return await menu(ctx, pages, controls, message=message,
+ page=page, timeout=timeout)
+
+
+async def prev_group(ctx: commands.Context, pages: list,
+ controls: dict, message: discord.Message, page: int,
+ timeout: float, emoji: str):
+ perms = message.channel.permissions_for(ctx.guild.me)
+ if perms.manage_messages: # Can manage messages, so remove react
+ try:
+ await message.remove_reaction(emoji, ctx.author)
+ except discord.NotFound:
+ pass
+ page = PAGE_GROUPS[bisect.bisect_left(PAGE_GROUPS, page) - 1]
+
+ return await menu(ctx, pages, controls, message=message,
+ page=page, timeout=timeout)
+
+
+def role_from_alignment(alignment):
+ return [role_embed(idx, role, ALIGNMENT_COLORS[role.alignment - 1])
+ for idx, role in enumerate(ROLE_LIST) if alignment == role.alignment]
+
+
+def role_from_category(category):
+ return [role_embed(idx, role, ALIGNMENT_COLORS[role.alignment - 1])
+ for idx, role in enumerate(ROLE_LIST) if category in role.category]
+
+
+def role_from_id(idx):
+ try:
+ role = ROLE_LIST[idx]
+ except IndexError:
+ return None
+
+ return role_embed(idx, role, ALIGNMENT_COLORS[role.alignment - 1])
+
+
+def role_from_name(name: str):
+ return [role_embed(idx, role, ALIGNMENT_COLORS[role.alignment - 1])
+ for idx, role in enumerate(ROLE_LIST) if name in role.__name__]
+
+
+def say_role_list(code_list, rand_roles):
+ roles = [ROLE_LIST[idx] for idx in code_list]
+ embed = discord.Embed(title="Currently selected roles")
+ role_dict = defaultdict(int)
+ for role in roles:
+ role_dict[str(role.__name__)] += 1
+
+ for role in rand_roles:
+ if 0 < role <= 6:
+ role_dict["Town {}".format(ROLE_CATEGORIES[role])] += 1
+ if 10 < role <= 16:
+ role_dict["Werewolf {}".format(ROLE_CATEGORIES[role])] += 1
+ if 20 < role <= 26:
+ role_dict["Neutral {}".format(ROLE_CATEGORIES[role])] += 1
+
+ for k, v in role_dict.items():
+ embed.add_field(name=k, value="Count: {}".format(v), inline=True)
+
+ return embed
+
+
+class GameBuilder:
+
+ def __init__(self):
+ self.code = []
+ self.rand_roles = []
+ setup()
+
+ async def build_game(self, ctx: commands.Context):
+ new_controls = {
+ '⏪': prev_group,
+ "⬅": prev_page,
+ '☑': self.select_page,
+ "➡": next_page,
+ '⏩': next_group,
+ '📇': self.list_roles,
+ "❌": close_menu
+ }
+
+ await ctx.send("Browse through roles and add the ones you want using the check mark")
+
+ await menu(ctx, ROLE_PAGES, new_controls, timeout=60)
+
+ out = await encode(self.code, self.rand_roles)
+ return out
+
+ async def list_roles(self, ctx: commands.Context, pages: list,
+ controls: dict, message: discord.Message, page: int,
+ timeout: float, emoji: str):
+ perms = message.channel.permissions_for(ctx.guild.me)
+ if perms.manage_messages: # Can manage messages, so remove react
+ try:
+ await message.remove_reaction(emoji, ctx.author)
+ except discord.NotFound:
+ pass
+
+ await ctx.send(embed=say_role_list(self.code, self.rand_roles))
+
+ return await menu(ctx, pages, controls, message=message,
+ page=page, timeout=timeout)
+
+ async def select_page(self, ctx: commands.Context, pages: list,
+ controls: dict, message: discord.Message, page: int,
+ timeout: float, emoji: str):
+ perms = message.channel.permissions_for(ctx.guild.me)
+ if perms.manage_messages: # Can manage messages, so remove react
+ try:
+ await message.remove_reaction(emoji, ctx.author)
+ except discord.NotFound:
+ pass
+
+ if page >= len(ROLE_LIST):
+ self.rand_roles.append(CATEGORY_COUNT[page - len(ROLE_LIST)])
+ else:
+ self.code.append(page)
+
+ return await menu(ctx, pages, controls, message=message,
+ page=page, timeout=timeout)
diff --git a/werewolf/game.py b/werewolf/game.py
index 3a559ca..181c198 100644
--- a/werewolf/game.py
+++ b/werewolf/game.py
@@ -1,16 +1,23 @@
import asyncio
import random
+from typing import List, Any, Dict, Set, Union
import discord
+from redbot.core import commands
from werewolf.builder import parse_code
from werewolf.player import Player
+from werewolf.role import Role
+from werewolf.votegroup import VoteGroup
class Game:
"""
Base class to run a single game of Werewolf
"""
+ vote_groups: Dict[str, VoteGroup]
+ roles: List[Role]
+ players: List[Player]
default_secret_channel = {
"channel": None,
@@ -25,19 +32,14 @@ class Game:
day_vote_count = 3
- # def __new__(cls, guild, game_code):
- # game_code = ["VanillaWerewolf", "Villager", "Villager"]
- #
- # return super().__new__(cls, guild, game_code)
-
- def __init__(self, guild, role, game_code):
+ def __init__(self, guild: discord.Guild, role: discord.Role = None,
+ category: discord.CategoryChannel = None, village: discord.TextChannel = None,
+ log_channel: discord.TextChannel = None, game_code=None):
self.guild = guild
- self.game_code = ["VanillaWerewolf"]
- self.game_role = role
+ self.game_code = game_code
- self.roles = []
-
- self.players = []
+ self.roles = [] # List[Role]
+ self.players = [] # List[Player]
self.day_vote = {} # author: target
self.vote_totals = {} # id: total_votes
@@ -49,9 +51,15 @@ class Game:
self.day_time = False
self.day_count = 0
+ self.ongoing_vote = False
+
+ self.game_role = role # discord.Role
+ self.channel_category = category # discord.CategoryChannel
+ self.village_channel = village # discord.TextChannel
+ self.log_channel = log_channel
- self.channel_category = None
- self.village_channel = None
+ self.to_delete = set()
+ self.save_perms = {}
self.p_channels = {} # uses default_secret_channel
self.vote_groups = {} # ID : VoteGroup()
@@ -60,22 +68,22 @@ class Game:
self.loop = asyncio.get_event_loop()
- def __del__(self):
- """
- Cleanup channels as necessary
- :return:
- """
-
- print("Delete is called")
-
- self.game_over = True
- if self.village_channel:
- asyncio.ensure_future(self.village_channel.delete("Werewolf game-over"))
-
- for c_data in self.p_channels.values():
- asyncio.ensure_future(c_data["channel"].delete("Werewolf game-over"))
+ # def __del__(self):
+ # """
+ # Cleanup channels as necessary
+ # :return:
+ # """
+ #
+ # print("Delete is called")
+ #
+ # self.game_over = True
+ # if self.village_channel:
+ # asyncio.ensure_future(self.village_channel.delete("Werewolf game-over"))
+ #
+ # for c_data in self.p_channels.values():
+ # asyncio.ensure_future(c_data["channel"].delete("Werewolf game-over"))
- async def setup(self, ctx):
+ async def setup(self, ctx: commands.Context):
"""
Runs the initial setup
@@ -86,17 +94,34 @@ class Game:
4. Start game
"""
if self.game_code:
- await self.get_roles()
+ await self.get_roles(ctx)
if len(self.players) != len(self.roles):
- await ctx.send("Player count does not match role count, cannot start")
+ await ctx.send("Player count does not match role count, cannot start\n"
+ "Currently **{} / {}**\n"
+ "Use `{}ww code` to pick a new game"
+ "".format(len(self.players), len(self.roles), ctx.prefix))
self.roles = []
return False
if self.game_role is None:
- await ctx.send("Game role not configured, cannot start")
- self.roles = []
- return False
+ try:
+ self.game_role = await ctx.guild.create_role(name="WW Players",
+ hoist=True,
+ mentionable=True,
+ reason="(BOT) Werewolf game role")
+ self.to_delete.add(self.game_role)
+ except (discord.Forbidden, discord.HTTPException):
+ await ctx.send("Game role not configured and unable to generate one, cannot start")
+ self.roles = []
+ return False
+ try:
+ for player in self.players:
+ await player.member.add_roles(*[self.game_role])
+ except discord.Forbidden:
+ await ctx.send(
+ "Unable to add role **{}**\nBot is missing `manage_roles` permissions".format(self.game_role.name))
+ return False
await self.assign_roles()
@@ -104,21 +129,55 @@ class Game:
overwrite = {
self.guild.default_role: discord.PermissionOverwrite(read_messages=True, send_messages=False,
add_reactions=False),
- self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, add_reactions=True),
+ self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, add_reactions=True,
+ manage_messages=True, manage_channels=True,
+ manage_roles=True),
self.game_role: discord.PermissionOverwrite(read_messages=True, send_messages=True)
}
+ if self.channel_category is None:
+ self.channel_category = await self.guild.create_category("Werewolf Game",
+ overwrites=overwrite,
+ reason="(BOT) New game of werewolf")
+ else: # No need to modify categories
+ pass
+ # await self.channel_category.edit(name="🔴 Werewolf Game (ACTIVE)", reason="(BOT) New game of werewolf")
+ # for target, ow in overwrite.items():
+ # await self.channel_category.set_permissions(target=target,
+ # overwrite=ow,
+ # reason="(BOT) New game of werewolf")
+ if self.village_channel is None:
+ try:
+ self.village_channel = await self.guild.create_text_channel("🔵Werewolf",
+ overwrites=overwrite,
+ reason="(BOT) New game of werewolf",
+ category=self.channel_category)
+ except discord.Forbidden:
+ await ctx.send("Unable to create Game Channel and none was provided\n"
+ "Grant Bot appropriate permissions or assign a game_channel")
+ return False
+ else:
+ self.save_perms[self.village_channel] = self.village_channel.overwrites
+ try:
+ await self.village_channel.edit(name="🔵Werewolf",
+ category=self.channel_category,
+ reason="(BOT) New game of werewolf")
+ except discord.Forbidden as e:
+ print("Unable to rename Game Channel")
+ print(e)
+ await ctx.send("Unable to rename Game Channel, ignoring")
- self.channel_category = await self.guild.create_category("ww-game", overwrites=overwrite, reason="New game of "
- "werewolf")
-
- # for player in self.players:
- # overwrite[player.member] = discord.PermissionOverwrite(read_messages=True)
-
- self.village_channel = await self.guild.create_text_channel("Village Square",
- overwrites=overwrite,
- reason="New game of werewolf",
- category=self.channel_category)
-
+ try:
+ for target, ow in overwrite.items():
+ curr = self.village_channel.overwrites_for(target)
+ curr.update(**{perm: value for perm, value in ow})
+ await self.village_channel.set_permissions(target=target,
+ overwrite=curr,
+ reason="(BOT) New game of werewolf")
+ except discord.Forbidden:
+ await ctx.send("Unable to edit Game Channel permissions\n"
+ "Grant Bot appropriate permissions to manage permissions")
+ return
+ self.started = True
# Assuming everything worked so far
print("Pre at_game_start")
await self._at_game_start() # This will queue channels and votegroups to be made
@@ -127,7 +186,9 @@ class Game:
print("Channel id: " + channel_id)
overwrite = {
self.guild.default_role: discord.PermissionOverwrite(read_messages=False),
- self.guild.me: discord.PermissionOverwrite(read_messages=True)
+ self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, add_reactions=True,
+ manage_messages=True, manage_channels=True,
+ manage_roles=True)
}
for player in self.p_channels[channel_id]["players"]:
@@ -135,7 +196,7 @@ class Game:
channel = await self.guild.create_text_channel(channel_id,
overwrites=overwrite,
- reason="Ww game secret channel",
+ reason="(BOT) WW game secret channel",
category=self.channel_category)
self.p_channels[channel_id]["channel"] = channel
@@ -207,13 +268,15 @@ class Game:
return
self.can_vote = True
- await asyncio.sleep(12) # 4 minute days FixMe to 120 later
+ await asyncio.sleep(24) # 4 minute days FixMe to 120 later
if check():
return
await self.village_channel.send(embed=discord.Embed(title="**Two minutes of daylight remain...**"))
- await asyncio.sleep(12) # 4 minute days FixMe to 120 later
+ await asyncio.sleep(24) # 4 minute days FixMe to 120 later
# Need a loop here to wait for trial to end (can_vote?)
+ while self.ongoing_vote:
+ asyncio.sleep(5)
if check():
return
@@ -226,16 +289,17 @@ class Game:
data = {"player": target}
await self._notify(2, data)
+ self.ongoing_vote = True
+
self.used_votes += 1
- self.can_vote = False
- await self.speech_perms(self.village_channel, target.member)
+ await self.speech_perms(self.village_channel, target.member) # Only target can talk
await self.village_channel.send(
"**{} will be put to trial and has 30 seconds to defend themselves**".format(target.mention))
await asyncio.sleep(30)
- await self.speech_perms(self.village_channel, target.member, undo=True)
+ await self.speech_perms(self.village_channel, target.member, undo=True) # No one can talk
message = await self.village_channel.send(
"Everyone will now vote whether to lynch {}\n"
@@ -243,42 +307,46 @@ class Game:
"*Majority rules, no-lynch on ties, "
"vote both or neither to abstain, 15 seconds to vote*".format(target.mention))
- await self.village_channel.add_reaction("👍")
- await self.village_channel.add_reaction("👎")
+ await message.add_reaction("👍")
+ await message.add_reaction("👎")
await asyncio.sleep(15)
-
reaction_list = message.reactions
- up_votes = sum(p.emoji == "👍" and not p.me for p in reaction_list)
- down_votes = sum(p.emoji == "👎" and not p.me for p in reaction_list)
+ up_votes = sum(p for p in reaction_list if p.emoji == "👍" and not p.me)
+ down_votes = sum(p for p in reaction_list if p.emoji == "👎" and not p.me)
- if len(down_votes) > len(up_votes):
+ if down_votes > up_votes:
embed = discord.Embed(title="Vote Results", color=0xff0000)
else:
embed = discord.Embed(title="Vote Results", color=0x80ff80)
- embed.add_field(name="👎", value="**{}**".format(len(up_votes)), inline=True)
- embed.add_field(name="👍", value="**{}**".format(len(down_votes)), inline=True)
+ embed.add_field(name="👎", value="**{}**".format(up_votes), inline=True)
+ embed.add_field(name="👍", value="**{}**".format(down_votes), inline=True)
await self.village_channel.send(embed=embed)
- if len(down_votes) > len(up_votes):
+ if down_votes > up_votes:
await self.village_channel.send("**Voted to lynch {}!**".format(target.mention))
await self.lynch(target)
+ self.can_vote = False
else:
await self.village_channel.send("**{} has been spared!**".format(target.mention))
if self.used_votes >= self.day_vote_count:
await self.village_channel.send("**All votes have been used! Day is now over!**")
+ self.can_vote = False
else:
await self.village_channel.send(
"**{}**/**{}** of today's votes have been used!\n"
"Nominate carefully..".format(self.used_votes, self.day_vote_count))
- self.can_vote = True # Only re-enable voting if more votes remain
+
+ self.ongoing_vote = False
if not self.can_vote:
await self._at_day_end()
+ else:
+ await self.normal_perms(self.village_channel) # No point if about to be night
async def _at_kill(self, target): # ID 3
if self.game_over:
@@ -329,7 +397,7 @@ class Game:
return
await self._notify(7)
- await asyncio.sleep(15)
+ await asyncio.sleep(10)
await self._at_day_start()
async def _at_visit(self, target, source): # ID 8
@@ -355,16 +423,22 @@ class Game:
############END Notify structure############
- async def generate_targets(self, channel):
+ async def generate_targets(self, channel, with_roles=False):
embed = discord.Embed(title="Remaining Players")
for i in range(len(self.players)):
player = self.players[i]
if player.alive:
status = ""
else:
- status = "*Dead*"
- embed.add_field(name="ID# **{}**".format(i),
- value="{} {}".format(status, player.member.display_name), inline=True)
+ status = "*[Dead]*-"
+ if with_roles or not player.alive:
+ embed.add_field(name="ID# **{}**".format(i),
+ value="{}{}-{}".format(status, player.member.display_name, str(player.role)),
+ inline=True)
+ else:
+ embed.add_field(name="ID# **{}**".format(i),
+ value="{}{}".format(status, player.member.display_name),
+ inline=True)
return await channel.send(embed=embed)
@@ -400,6 +474,13 @@ class Game:
self.players.append(Player(member))
+ if self.game_role is not None:
+ try:
+ await member.add_roles(*[self.game_role])
+ except discord.Forbidden:
+ await channel.send(
+ "Unable to add role **{}**\nBot is missing `manage_roles` permissions".format(self.game_role.name))
+
await channel.send("{} has been added to the game, "
"total players is **{}**".format(member.mention, len(self.players)))
@@ -417,6 +498,7 @@ class Game:
await channel.send("{} has left the game".format(member.mention))
else:
self.players = [player for player in self.players if player.member != member]
+ await member.remove_roles(*[self.game_role])
await channel.send("{} chickened out, player count is now **{}**".format(member.mention, len(self.players)))
async def choose(self, ctx, data):
@@ -431,7 +513,7 @@ class Game:
return
if not player.alive:
- await ctx.send("**Corpses** can't vote...")
+ await ctx.send("**Corpses** can't participate...")
return
if player.role.blocked:
@@ -441,7 +523,7 @@ class Game:
# Let role do target validation, might be alternate targets
# I.E. Go on alert? y/n
- await player.choose(ctx, data)
+ await player.role.choose(ctx, data)
async def _visit(self, target, source):
await target.role.visit(source)
@@ -471,7 +553,7 @@ class Game:
return
if not player.alive:
- await channel.send("Corpses can't vote")
+ await channel.send("Corpses can't vote...")
return
if channel == self.village_channel:
@@ -531,7 +613,9 @@ class Game:
out = "**{ID}** - " + method
return out.format(ID=target.id, target=target.member.display_name)
else:
- return "**{ID}** - {target} was found dead".format(ID=target.id, target=target.member.display_name)
+ return "**{ID}** - {target} the {role} was found dead".format(ID=target.id,
+ target=target.member.display_name,
+ role=await target.role.get_role())
async def _quit(self, player):
"""
@@ -558,6 +642,7 @@ class Game:
target = await self.get_day_target(target_id, source)
else:
target = await self.get_night_target(target_id, source)
+
if source is not None:
if source.role.blocked:
# Do nothing if blocked, blocker handles text
@@ -595,14 +680,25 @@ class Game:
async def get_day_target(self, target_id, source=None):
return self.players[target_id] # ToDo check source
- async def get_roles(self, game_code=None):
+ async def set_code(self, ctx: commands.Context, game_code):
+ if game_code is not None:
+ self.game_code = game_code
+ await ctx.send("Code has been set")
+
+ async def get_roles(self, ctx, game_code=None):
if game_code is not None:
self.game_code = game_code
if self.game_code is None:
return False
- self.roles = await parse_code(self.game_code)
+ try:
+ self.roles = await parse_code(self.game_code, self)
+ except ValueError as e:
+ await ctx.send("Invalid Code: Code contains unknown character\n{}".format(e))
+ return False
+ except IndexError as e:
+ await ctx.send("Invalid Code: Code references unknown role\n{}".format(e))
if not self.roles:
return False
@@ -613,11 +709,10 @@ class Game:
self.players.sort(key=lambda pl: pl.member.display_name.lower())
if len(self.roles) != len(self.players):
- await self.village_channel("Unhandled error - roles!=players")
+ await self.village_channel.send("Unhandled error - roles!=players")
return False
for idx, role in enumerate(self.roles):
- self.roles[idx] = role(self)
await self.roles[idx].assign_player(self.players[idx])
# Sorted players, now assign id's
await self.players[idx].assign_id(idx)
@@ -645,28 +740,67 @@ class Game:
await channel.set_permissions(self.game_role, read_messages=True, send_messages=False)
await channel.set_permissions(member, send_messages=True)
- async def normal_perms(self, channel, member_list):
+ async def normal_perms(self, channel):
await channel.set_permissions(self.game_role, read_messages=True, send_messages=True)
- # for member in member_list:
- # await channel.set_permissions(member, read_messages=True)
async def _check_game_over(self):
- alive_players = [player for player self.players if player.alive]
-
- if len(alive_players)<=2:
+ # return # ToDo: re-enable game-over checking
+ alive_players = [player for player in self.players if player.alive]
+
+ if len(alive_players) <= 0:
+ await self.village_channel.send(embed=discord.Embed(title="**Everyone is dead! Game Over!**"))
+ self.game_over = True
+ elif len(alive_players) == 1:
+ self.game_over = True
+ await self._announce_winners(alive_players)
+ elif len(alive_players) == 2:
# Check 1v1 victory conditions ToDo
- pass
+ self.game_over = True
+ alignment1 = alive_players[0].role.alignment
+ alignment2 = alive_players[1].role.alignment
+ if alignment1 == alignment2: # Same team
+ winners = alive_players
+ else:
+ winners = [max(alive_players, key=lambda p: p.role.alignment)]
+
+ await self._announce_winners(winners)
else:
- #Check if everyone is on the same team
- alignment = alive_players[0].role.alignment
+ # Check if everyone is on the same team
+ alignment = alive_players[0].role.alignment # Get first allignment and compare to rest
for player in alive_players:
if player.role.alignment != alignment:
- return False
+ return
# Only remaining team wins
+ self.game_over = True
+ await self._announce_winners(alive_players)
+
+ # If no return, cleanup and end game
+ await self._end_game()
+
+ async def _announce_winners(self, winnerlist):
+ await self.village_channel.send(self.game_role.mention)
+ embed = discord.Embed(title='Game Over', description='The Following Players have won!')
+ for player in winnerlist:
+ embed.add_field(name=player.member.display_name, value=str(player.role), inline=True)
+ embed.set_thumbnail(url='https://emojipedia-us.s3.amazonaws.com/thumbs/160/twitter/134/trophy_1f3c6.png')
+ await self.village_channel.send(embed=embed)
-
+ await self.generate_targets(self.village_channel, True)
async def _end_game(self):
- # ToDo
- pass
+ # Remove game_role access for potential archiving for now
+ reason = '(BOT) End of WW game'
+ for obj in self.to_delete:
+ print(obj)
+ await obj.delete(reason=reason)
+
+ try:
+ await self.village_channel.edit(reason=reason, name="Werewolf")
+ for target, overwrites in self.save_perms[self.village_channel]:
+ await self.village_channel.set_permissions(target, overwrite=overwrites, reason=reason)
+ await self.village_channel.set_permissions(self.game_role, overwrite=None, reason=reason)
+ except (discord.HTTPException, discord.NotFound, discord.errors.NotFound):
+ pass
+
+ # Optional dynamic channels/categories
diff --git a/werewolf/info.json b/werewolf/info.json
index d46e1d2..5fbc50b 100644
--- a/werewolf/info.json
+++ b/werewolf/info.json
@@ -9,7 +9,7 @@
],
"description": "Customizable Werewolf Game",
"hidden": false,
- "install_msg": "Thank you for installing Werewolf! Use [p]wwset to run inital setup",
+ "install_msg": "Thank you for installing Werewolf! Get started with `[p]load werewolf`\n Use `[p]wwset` to run inital setup",
"requirements": [],
"short": "Werewolf Game",
"tags": [
diff --git a/werewolf/night_powers.py b/werewolf/night_powers.py
new file mode 100644
index 0000000..215e8eb
--- /dev/null
+++ b/werewolf/night_powers.py
@@ -0,0 +1,23 @@
+from werewolf.role import Role
+
+
+def night_immune(role: Role):
+ role.player.alive = True
+
+
+async def pick_target(role: Role, ctx, data):
+ if not role.player.alive: # FixMe: Game handles this?
+ await role.player.send_dm("You're already dead!")
+ return None
+
+ target_id = int(data)
+ try:
+ target = role.game.players[target_id]
+ except IndexError:
+ target = None
+
+ if target is None:
+ await ctx.send("Not a valid ID")
+ return None
+
+ return target_id, target
diff --git a/werewolf/player.py b/werewolf/player.py
index 78710aa..c84d87f 100644
--- a/werewolf/player.py
+++ b/werewolf/player.py
@@ -27,4 +27,7 @@ class Player:
self.id = target_id
async def send_dm(self, message):
- await self.member.send(message) # Lets do embeds later
+ try:
+ await self.member.send(message) # Lets do embeds later
+ except discord.Forbidden:
+ await self.role.game.village_channel.send("Couldn't DM {}, uh oh".format(self.mention))
diff --git a/werewolf/role.py b/werewolf/role.py
index 64c78a4..3e4124d 100644
--- a/werewolf/role.py
+++ b/werewolf/role.py
@@ -46,6 +46,12 @@ class Role:
"You win by testing the game\n"
"Lynch players during the day with `[p]ww vote `"
)
+ description = (
+ "This is the basic role\n"
+ "All roles are based on this Class\n"
+ "Has no special significance"
+ )
+ icon_url = None # Adding a URL here will enable a thumbnail of the role
def __init__(self, game):
self.game = game
@@ -65,6 +71,9 @@ class Role:
(self._at_visit, 0)
]
+ def __repr__(self):
+ return self.__class__.__name__
+
async def on_event(self, event, data):
"""
See Game class for event guide
@@ -101,14 +110,14 @@ class Role:
Interaction for powerful access of role
Unlikely to be able to deceive this
"""
- return "Default"
+ return "Role"
async def see_role(self, source=None):
"""
Interaction for investigative roles.
More common to be able to deceive this action
"""
- return "Role"
+ return "Default"
async def _at_game_start(self, data=None):
if self.channel_id:
@@ -144,7 +153,7 @@ class Role:
"""
Called when someone is trying to kill you!
Can you do anything about it?
- self.alive is now set to False, set to True to stay alive
+ self.player.alive is now set to False, set to True to stay alive
"""
pass
diff --git a/werewolf/roles/seer.py b/werewolf/roles/seer.py
index ccd61be..63b62a2 100644
--- a/werewolf/roles/seer.py
+++ b/werewolf/roles/seer.py
@@ -1,8 +1,9 @@
+from werewolf.night_powers import pick_target
from werewolf.role import Role
class Seer(Role):
- rand_choice = False # Determines if it can be picked as a random role (False for unusually disruptive roles)
+ rand_choice = True # Determines if it can be picked as a random role (False for unusually disruptive roles)
category = [1, 2] # List of enrolled categories (listed above)
alignment = 1 # 1: Town, 2: Werewolf, 3: Neutral
channel_id = "" # Empty for no private channel
@@ -13,6 +14,8 @@ class Seer(Role):
"Lynch players during the day with `[p]ww vote `\n"
"Check for werewolves at night with `[p]ww choose `"
)
+ description = "A mystic in search of answers in a chaotic town.\n" \
+ "Calls upon the cosmos to discern those of Lycan blood"
def __init__(self, game):
super().__init__(game)
@@ -29,33 +32,10 @@ class Seer(Role):
(self._at_hang, 0),
(self._at_day_end, 0),
(self._at_night_start, 2),
- (self._at_night_end, 4)
+ (self._at_night_end, 4),
+ (self._at_visit, 0)
]
- # async def on_event(self, event, data):
- # """
- # See Game class for event guide
- # """
- #
- # await self.action_list[event][0](data)
- #
- #
- # async def assign_player(self, player):
- # """
- # Give this role a player
- # Can be used after the game has started (Cult, Mason, other role swap)
- # """
- #
- # player.role = self
- # self.player = player
- #
- # async def get_alignment(self, source=None):
- # """
- # Interaction for power access of team (Village, Werewolf, Other)
- # Unlikely to be able to deceive this
- # """
- # return self.alignment
-
async def see_alignment(self, source=None):
"""
Interaction for investigative roles attempting
@@ -77,34 +57,23 @@ class Seer(Role):
"""
return "Villager"
- # async def _at_game_start(self, data=None):
- # pass
- #
- # async def _at_day_start(self, data=None):
- # pass
- #
- # async def _at_voted(self, target=None):
- # pass
- #
- # async def _at_kill(self, target=None):
- # pass
- #
- # async def _at_hang(self, target=None):
- # pass
- #
- # async def _at_day_end(self):
- # pass
-
async def _at_night_start(self, data=None):
+ if not self.player.alive:
+ return
+ self.see_target = None
await self.game.generate_targets(self.player.member)
- await self.player.send_dm("{}\n**Pick a target to see tonight**\n")
+ await self.player.send_dm("**Pick a target to see tonight**")
async def _at_night_end(self, data=None):
- target = await self.game.visit(self.see_target)
+ if self.see_target is None:
+ if self.player.alive:
+ await self.player.send_dm("You will not use your powers tonight...")
+ return
+ target = await self.game.visit(self.see_target, self.player)
alignment = None
if target:
- alignment = await target.see_alignment(self.player)
+ alignment = await target.role.see_alignment(self.player)
if alignment == "Werewolf":
out = "Your insight reveals this player to be a **Werewolf!**"
@@ -113,35 +82,9 @@ class Seer(Role):
await self.player.send_dm(out)
- # async def _at_visit(self, data=None):
- # pass
- #
- # async def kill(self, source):
- # """
- # Called when someone is trying to kill you!
- # Can you do anything about it?
- # self.alive is now set to False, set to True to stay alive
- # """
- # pass
- #
- # async def visit(self, source):
- # """
- # Called whenever a night action targets you
- # Source is the player who visited you
- # """
- # pass
-
async def choose(self, ctx, data):
"""Handle night actions"""
- target_id = int(data)
- try:
- target = self.game.players[target_id]
- except IndexError:
- target = None
-
- if target is None:
- await ctx.send("Not a valid ID")
- return
+ await super().choose(ctx, data)
- self.see_target = target_id
+ self.see_target, target = await pick_target(self, ctx, data)
await ctx.send("**You will attempt to see the role of {} tonight...**".format(target.member.display_name))
diff --git a/werewolf/roles/shifter.py b/werewolf/roles/shifter.py
new file mode 100644
index 0000000..d7ba956
--- /dev/null
+++ b/werewolf/roles/shifter.py
@@ -0,0 +1,129 @@
+from werewolf.night_powers import pick_target
+from werewolf.role import Role
+
+
+class Shifter(Role):
+ """
+ Base Role class for werewolf game
+
+ Category enrollment guide as follows (category property):
+ Town:
+ 1: Random, 2: Investigative, 3: Protective, 4: Government,
+ 5: Killing, 6: Power (Special night action)
+
+ Werewolf:
+ 11: Random, 12: Deception, 15: Killing, 16: Support
+
+ Neutral:
+ 21: Benign, 22: Evil, 23: Killing
+
+
+ Example category:
+ category = [1, 5, 6] Could be Veteran
+ category = [1, 5] Could be Bodyguard
+ category = [11, 16] Could be Werewolf Silencer
+
+
+ Action guide as follows (on_event function):
+ _at_night_start
+ 0. No Action
+ 1. Detain actions (Jailer/Kidnapper)
+ 2. Group discussions and choose targets
+
+ _at_night_end
+ 0. No Action
+ 1. Self actions (Veteran)
+ 2. Target switching and role blocks (bus driver, witch, escort)
+ 3. Protection / Preempt actions (bodyguard/framer)
+ 4. Non-disruptive actions (seer/silencer)
+ 5. Disruptive actions (Killing)
+ 6. Role altering actions (Cult / Mason)
+ """
+
+ rand_choice = False # Determines if it can be picked as a random role (False for unusually disruptive roles)
+ category = [22] # List of enrolled categories (listed above)
+ alignment = 3 # 1: Town, 2: Werewolf, 3: Neutral
+ channel_id = "" # Empty for no private channel
+ unique = False # Only one of this role per game
+ game_start_message = (
+ "Your role is **Shifter**\n"
+ "You have no win condition (yet)\n"
+ "Swap your role with other players during the night using `[p]ww choose `\n"
+ "Lynch players during the day with `[p]ww vote `"
+ )
+ description = (
+ "A creature of unknown origin seeks to escape it's ethereal nightmare\n"
+ "It's curse cannot be broken, but transfers are allowed"
+ )
+ icon_url = None # Adding a URL here will enable a thumbnail of the role
+
+ def __init__(self, game):
+ super().__init__(game)
+
+ self.shift_target = None
+ self.action_list = [
+ (self._at_game_start, 1), # (Action, Priority)
+ (self._at_day_start, 0),
+ (self._at_voted, 0),
+ (self._at_kill, 0),
+ (self._at_hang, 0),
+ (self._at_day_end, 0),
+ (self._at_night_start, 2), # Chooses targets
+ (self._at_night_end, 6), # Role Swap
+ (self._at_visit, 0)
+ ]
+
+ async def see_alignment(self, source=None):
+ """
+ Interaction for investigative roles attempting
+ to see alignment (Village, Werewolf, Other)
+ """
+ return "Other"
+
+ async def get_role(self, source=None):
+ """
+ Interaction for powerful access of role
+ Unlikely to be able to deceive this
+ """
+ return "Shifter"
+
+ async def see_role(self, source=None):
+ """
+ Interaction for investigative roles.
+ More common to be able to deceive this action
+ """
+ return "Shifter"
+
+ async def _at_night_start(self, data=None):
+ await super()._at_night_start(data)
+ self.shift_target = None
+ await self.game.generate_targets(self.player.member)
+ await self.player.send_dm("**Pick a target to shift into**")
+
+ async def _at_night_end(self, data=None):
+ await super()._at_night_end(data)
+ if self.shift_target is None:
+ if self.player.alive:
+ await self.player.send_dm("You will not use your powers tonight...")
+ return
+ target = await self.game.visit(self.shift_target, self.player)
+
+ if target and target.player.alive:
+ await target.role.assign_player(self.player)
+ await self.assign_player(target)
+
+ # Roles have now been swapped
+
+ await self.player.send_dm("Your role has been stolen...\n"
+ "You are now a **Shifter**.")
+ await self.player.send_dm(self.game_start_message)
+
+ await target.send_dm(target.role.game_start_message)
+ else:
+ await self.player.send_dm("**Your shift failed...**")
+ async def choose(self, ctx, data):
+ """Handle night actions"""
+ await super().choose(ctx, data)
+
+ self.shift_target, target = await pick_target(self, ctx, data)
+ await ctx.send("**You will attempt to see the role of {} tonight...**".format(target.member.display_name))
diff --git a/werewolf/roles/vanillawerewolf.py b/werewolf/roles/vanillawerewolf.py
index d780eb8..5abce61 100644
--- a/werewolf/roles/vanillawerewolf.py
+++ b/werewolf/roles/vanillawerewolf.py
@@ -31,29 +31,6 @@ class VanillaWerewolf(Role):
(self._at_visit, 0)
]
- # async def on_event(self, event, data):
- # """
- # See Game class for event guide
- # """
-
- # await self.action_list[event][0](data)
-
- # async def assign_player(self, player):
- # """
- # Give this role a player
- # Can be used after the game has started (Cult, Mason, role swap)
- # """
-
- # player.role = self
- # self.player = player
-
- # async def get_alignment(self, source=None):
- # """
- # Interaction for power access of team (Village, Werewolf, Other)
- # Unlikely to be able to deceive this
- # """
- # return self.alignment
-
async def see_alignment(self, source=None):
"""
Interaction for investigative roles attempting
@@ -82,45 +59,6 @@ class VanillaWerewolf(Role):
await self.player.send_dm(self.game_start_message)
- # async def _at_day_start(self, data=None):
- # super()._at_day_start(data)
-
- # async def _at_voted(self, data=None):
- # super()._at_voted(data)
-
- # async def _at_kill(self, data=None):
- # super()._at_kill(data)
-
- # async def _at_hang(self, data=None):
- # super()._at_hang(data)
-
- # async def _at_day_end(self, data=None):
- # super()._at_day_end(data)
-
- # async def _at_night_start(self, data=None):
- # super()._at_night_start(data)
-
- # async def _at_night_end(self, data=None):
- # super()._at_night_end(data)
-
- # async def _at_visit(self, data=None):
- # pass
-
- # async def kill(self, source):
- # """
- # Called when someone is trying to kill you!
- # Can you do anything about it?
- # self.alive is now set to False, set to True to stay alive
- # """
- # pass
-
- # async def visit(self, source):
- # """
- # Called whenever a night action targets you
- # Source is the player who visited you
- # """
- # pass
-
async def choose(self, ctx, data):
"""Handle night actions"""
await self.player.member.send("Use `[p]ww vote` in your werewolf channel")
diff --git a/werewolf/roles/villager.py b/werewolf/roles/villager.py
index f1b8016..040e34d 100644
--- a/werewolf/roles/villager.py
+++ b/werewolf/roles/villager.py
@@ -2,7 +2,7 @@ from werewolf.role import Role
class Villager(Role):
- rand_choice = False # Determines if it can be picked as a random role (False for unusually disruptive roles)
+ rand_choice = True # Determines if it can be picked as a random role (False for unusually disruptive roles)
category = [1] # List of enrolled categories (listed above)
alignment = 1 # 1: Town, 2: Werewolf, 3: Neutral
channel_id = "" # Empty for no private channel
@@ -15,46 +15,6 @@ class Villager(Role):
def __init__(self, game):
super().__init__(game)
- # self.game = game
- # self.player = None
- # self.blocked = False
- # self.properties = {} # Extra data for other roles (i.e. arsonist)
- #
- # self.action_list = [
- # (self._at_game_start, 0), # (Action, Priority)
- # (self._at_day_start, 0),
- # (self._at_voted, 0),
- # (self._at_kill, 0),
- # (self._at_hang, 0),
- # (self._at_day_end, 0),
- # (self._at_night_start, 0),
- # (self._at_night_end, 0),
- # (self._at_visit, 0)
- # ]
-
- # async def on_event(self, event, data):
- # """
- # See Game class for event guide
- # """
- #
- # await self.action_list[event][0](data)
- #
- #
- # async def assign_player(self, player):
- # """
- # Give this role a player
- # Can be used after the game has started (Cult, Mason, other role swap)
- # """
- #
- # player.role = self
- # self.player = player
- #
- # async def get_alignment(self, source=None):
- # """
- # Interaction for power access of team (Village, Werewolf, Other)
- # Unlikely to be able to deceive this
- # """
- # return self.alignment
async def see_alignment(self, source=None):
"""
@@ -76,49 +36,3 @@ class Villager(Role):
More common to be able to deceive these roles
"""
return "Villager"
-
- # async def _at_game_start(self, data=None):
- # pass
- #
- # async def _at_day_start(self, data=None):
- # pass
- #
- # async def _at_voted(self, target=None):
- # pass
- #
- # async def _at_kill(self, target=None):
- # pass
- #
- # async def _at_hang(self, target=None):
- # pass
- #
- # async def _at_day_end(self):
- # pass
- #
- # async def _at_night_start(self):
- # pass
- #
- # async def _at_night_end(self):
- # pass
- #
- # async def _at_visit(self, data=None):
- # pass
- #
- # async def kill(self, source):
- # """
- # Called when someone is trying to kill you!
- # Can you do anything about it?
- # self.alive is now set to False, set to True to stay alive
- # """
- # pass
- #
- # async def visit(self, source):
- # """
- # Called whenever a night action targets you
- # Source is the player who visited you
- # """
- # pass
- #
- # async def choose(self, ctx, data):
- # """Handle night actions"""
- # pass
diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py
index c5bf57e..17634ec 100644
--- a/werewolf/werewolf.py
+++ b/werewolf/werewolf.py
@@ -1,22 +1,32 @@
import discord
-from discord.ext import commands
-from redbot.core import Config
-from redbot.core import RedContext
+from redbot.core import Config, checks
+
+from redbot.core.bot import Red
+from redbot.core import commands
+
+from werewolf.builder import GameBuilder, role_from_name, role_from_alignment, role_from_category, role_from_id
from werewolf.game import Game
+from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
+from typing import Any
+Cog: Any = getattr(commands, "Cog", object)
-class Werewolf:
+
+class Werewolf(Cog):
"""
Base to host werewolf on a guild
"""
- def __init__(self, bot):
+ def __init__(self, bot: Red):
self.bot = bot
self.config = Config.get_conf(self, identifier=87101114101119111108102, force_registration=True)
default_global = {}
default_guild = {
- "role": None
+ "role_id": None,
+ "category_id": None,
+ "channel_id": None,
+ "log_channel_id": None
}
self.config.register_global(**default_global)
@@ -29,54 +39,128 @@ class Werewolf:
for game in self.games.values():
del game
+ @commands.command()
+ async def buildgame(self, ctx: commands.Context):
+ gb = GameBuilder()
+ code = await gb.build_game(ctx)
+
+ if code != "":
+ await ctx.send("Your game code is **{}**".format(code))
+ else:
+ await ctx.send("No code generated")
+
+ @checks.guildowner()
@commands.group()
- async def wwset(self, ctx: RedContext):
+ async def wwset(self, ctx: commands.Context):
"""
Base command to adjust settings. Check help for command list.
"""
if ctx.invoked_subcommand is None:
- await ctx.send_help()
+ pass
+
+ @commands.guild_only()
+ @wwset.command(name="list")
+ async def wwset_list(self, ctx: commands.Context):
+ """
+ Lists current guild settings
+ """
+ success, role, category, channel, log_channel = await self._get_settings(ctx)
+ if not success:
+ await ctx.send("Failed to get settings")
+ return None
+
+ embed = discord.Embed(title="Current Guild Settings")
+ embed.add_field(name="Role", value=str(role))
+ embed.add_field(name="Category", value=str(category))
+ embed.add_field(name="Channel", value=str(channel))
+ embed.add_field(name="Log Channel", value=str(log_channel))
+ await ctx.send(embed=embed)
@commands.guild_only()
@wwset.command(name="role")
- async def wwset_role(self, ctx, role: discord.Role):
+ async def wwset_role(self, ctx: commands.Context, role: discord.Role=None):
"""
Assign the game role
This role should not be manually assigned
"""
- await self.config.guild(ctx.guild).role.set(role.id)
- await ctx.send("Game role has been set to **{}**".format(role.name))
+ if role is None:
+ await self.config.guild(ctx.guild).role_id.set(None)
+ await ctx.send("Cleared Game Role")
+ else:
+ await self.config.guild(ctx.guild).role_id.set(role.id)
+ await ctx.send("Game Role has been set to **{}**".format(role.name))
+
+ @commands.guild_only()
+ @wwset.command(name="category")
+ async def wwset_category(self, ctx: commands.Context, category_id=None):
+ """
+ Assign the channel category
+ """
+ if category_id is None:
+ await self.config.guild(ctx.guild).category_id.set(None)
+ await ctx.send("Cleared Game Channel Category")
+ else:
+ category = discord.utils.get(ctx.guild.categories, id=int(category_id))
+ if category is None:
+ await ctx.send("Category not found")
+ return
+ await self.config.guild(ctx.guild).category_id.set(category.id)
+ await ctx.send("Game Channel Category has been set to **{}**".format(category.name))
+
+ @commands.guild_only()
+ @wwset.command(name="channel")
+ async def wwset_channel(self, ctx: commands.Context, channel: discord.TextChannel=None):
+ """
+ Assign the village channel
+ """
+ if channel is None:
+ await self.config.guild(ctx.guild).channel_id.set(None)
+ await ctx.send("Cleared Game Channel")
+ else:
+ await self.config.guild(ctx.guild).channel_id.set(channel.id)
+ await ctx.send("Game Channel has been set to **{}**".format(channel.mention))
+
+ @commands.guild_only()
+ @wwset.command(name="logchannel")
+ async def wwset_log_channel(self, ctx: commands.Context, channel: discord.TextChannel=None):
+ """
+ Assign the log channel
+ """
+ if channel is None:
+ await self.config.guild(ctx.guild).log_channel_id.set(None)
+ await ctx.send("Cleared Game Log Channel")
+ else:
+ await self.config.guild(ctx.guild).log_channel_id.set(channel.id)
+ await ctx.send("Game Log Channel has been set to **{}**".format(channel.mention))
@commands.group()
- async def ww(self, ctx: RedContext):
+ async def ww(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()
+ pass
@commands.guild_only()
- @ww.command()
- async def new(self, ctx, game_code):
+ @ww.command(name="new")
+ async def ww_new(self, ctx: commands.Context, game_code=None):
"""
Create and join a new game of Werewolf
"""
-
- game = await self._get_game(ctx.guild, game_code)
-
+ game = await self._get_game(ctx, game_code)
if not game:
await ctx.send("Failed to start a new game")
else:
- await ctx.send("New game has started")
+ await ctx.send("Game is ready to join! Use `[p]ww join`")
@commands.guild_only()
- @ww.command()
- async def join(self, ctx):
+ @ww.command(name="join")
+ async def ww_join(self, ctx: commands.Context):
"""
Joins a game of Werewolf
"""
- game = await self._get_game(ctx.guild)
+ game = await self._get_game(ctx)
if not game:
await ctx.send("No game to join!\nCreate a new one with `[p]ww new`")
@@ -85,49 +169,71 @@ class Werewolf:
await game.join(ctx.author, ctx.channel)
@commands.guild_only()
- @ww.command()
- async def quit(self, ctx):
+ @ww.command(name="code")
+ async def ww_code(self, ctx: commands.Context, code):
+ """
+ Adjust game code
+ """
+
+ game = await self._get_game(ctx)
+
+ if not game:
+ await ctx.send("No game to join!\nCreate a new one with `[p]ww new`")
+ return
+
+ await game.set_code(ctx, code)
+
+ @commands.guild_only()
+ @ww.command(name="quit")
+ async def ww_quit(self, ctx: commands.Context):
"""
Quit a game of Werewolf
"""
- game = await self._get_game(ctx.guild)
+ game = await self._get_game(ctx)
await game.quit(ctx.author, ctx.channel)
@commands.guild_only()
- @ww.command()
- async def start(self, ctx):
+ @ww.command(name="start")
+ async def ww_start(self, ctx: commands.Context):
"""
Checks number of players and attempts to start the game
"""
- game = await self._get_game(ctx.guild)
+ game = await self._get_game(ctx)
if not game:
await ctx.send("No game running, cannot start")
- await game.setup(ctx)
+ if not await game.setup(ctx):
+ pass # Do something?
@commands.guild_only()
- @ww.command()
- async def stop(self, ctx):
+ @ww.command(name="stop")
+ async def ww_stop(self, ctx: commands.Context):
"""
Stops the current game
"""
- game = await self._get_game(ctx.guild)
- if not game:
- await ctx.send("No game running, cannot stop")
+ if ctx.guild is None:
+ # Private message, can't get guild
+ await ctx.send("Cannot start game from PM!")
+ return
+ if ctx.guild.id not in self.games or self.games[ctx.guild.id].game_over:
+ await ctx.send("No game to stop")
+ return
+ game = await self._get_game(ctx)
game.game_over = True
+ await ctx.send("Game has been stopped")
@commands.guild_only()
- @ww.command()
- async def vote(self, ctx, target_id: int):
+ @ww.command(name="vote")
+ async def ww_vote(self, ctx: commands.Context, target_id: int):
"""
Vote for a player by ID
"""
try:
target_id = int(target_id)
- except:
+ except ValueError:
target_id = None
if target_id is None:
@@ -145,7 +251,7 @@ class Werewolf:
# return
# else:
- game = await self._get_game(ctx.guild)
+ game = await self._get_game(ctx)
if game is None:
await ctx.send("No game running, cannot vote")
@@ -160,8 +266,8 @@ class Werewolf:
else:
await ctx.send("Nothing to vote for in this channel")
- @ww.command()
- async def choose(self, ctx, data):
+ @ww.command(name="choose")
+ async def ww_choose(self, ctx: commands.Context, data):
"""
Arbitrary decision making
Handled by game+role
@@ -171,7 +277,6 @@ class Werewolf:
if ctx.guild is not None:
await ctx.send("This action is only available in DM's")
return
-
# DM nonsense, find their game
# If multiple games, panic
for game in self.games.values():
@@ -183,20 +288,107 @@ class Werewolf:
await game.choose(ctx, data)
- async def _get_game(self, guild, game_code=None):
+ @ww.group(name="search")
+ async def ww_search(self, ctx: commands.Context):
+ """
+ Find custom roles by name, alignment, category, or ID
+ """
+ if ctx.invoked_subcommand is None or ctx.invoked_subcommand == self.ww_search:
+ await ctx.send_help()
+
+ @ww_search.command(name="name")
+ async def ww_search_name(self, ctx: commands.Context, *, name):
+ """Search for a role by name"""
+ if name is not None:
+ from_name = role_from_name(name)
+ if from_name:
+ await menu(ctx, from_name, DEFAULT_CONTROLS)
+ else:
+ await ctx.send("No roles containing that name were found")
+
+ @ww_search.command(name="alignment")
+ async def ww_search_alignment(self, ctx: commands.Context, alignment: int):
+ """Search for a role by alignment"""
+ if alignment is not None:
+ from_alignment = role_from_alignment(alignment)
+ if from_alignment:
+ await menu(ctx, from_alignment, DEFAULT_CONTROLS)
+ else:
+ await ctx.send("No roles with that alignment were found")
+
+ @ww_search.command(name="category")
+ async def ww_search_category(self, ctx: commands.Context, category: int):
+ """Search for a role by category"""
+ if category is not None:
+ pages = role_from_category(category)
+ if pages:
+ await menu(ctx, pages, DEFAULT_CONTROLS)
+ else:
+ await ctx.send("No roles in that category were found")
+
+ @ww_search.command(name="index")
+ async def ww_search_index(self, ctx: commands.Context, idx: int):
+ """Search for a role by ID"""
+ if idx is not None:
+ idx_embed = role_from_id(idx)
+ if idx_embed is not None:
+ await ctx.send(embed=idx_embed)
+ else:
+ await ctx.send("Role ID not found")
+
+ async def _get_game(self, ctx: commands.Context, game_code=None):
+ guild: discord.Guild = ctx.guild
+
if guild is None:
# Private message, can't get guild
+ await ctx.send("Cannot start game from PM!")
return None
- if guild.id not in self.games:
- if not game_code:
- return None
- role = await self.config.guild(guild).role()
- role = discord.utils.get(guild.roles, id=role)
- if role is None:
+ if guild.id not in self.games or self.games[guild.id].game_over:
+ await ctx.send("Starting a new game...")
+ success, role, category, channel, log_channel = await self._get_settings(ctx)
+
+ if not success:
+ await ctx.send("Cannot start a new game")
return None
- self.games[guild.id] = Game(guild, role, game_code)
+
+ self.games[guild.id] = Game(guild, role, category, channel, log_channel, game_code)
return self.games[guild.id]
async def _game_start(self, game):
await game.start()
+
+ async def _get_settings(self, ctx):
+ guild = ctx.guild
+ role = None
+ category = None
+ channel = None
+ log_channel = None
+
+ role_id = await self.config.guild(guild).role_id()
+ category_id = await self.config.guild(guild).category_id()
+ channel_id = await self.config.guild(guild).channel_id()
+ log_channel_id = await self.config.guild(guild).log_channel_id()
+
+ if role_id is not None:
+ role = discord.utils.get(guild.roles, id=role_id)
+ if role is None:
+ await ctx.send("Game Role is invalid")
+ return False, None, None, None, None
+ if category_id is not None:
+ category = discord.utils.get(guild.categories, id=category_id)
+ if category is None:
+ await ctx.send("Game Category is invalid")
+ return False, None, None, None, None
+ if channel_id is not None:
+ channel = discord.utils.get(guild.text_channels, id=channel_id)
+ if channel is None:
+ await ctx.send("Village Channel is invalid")
+ return False, None, None, None, None
+ if log_channel_id is not None:
+ log_channel = discord.utils.get(guild.text_channels, id=log_channel_id)
+ if log_channel is None:
+ await ctx.send("Log Channel is invalid")
+ return False, None, None, None, None
+
+ return True, role, category, channel, log_channel