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 timesCommissioned release, so suggestions will not be accepted
| +| audiotrivia | **Alpha** |
Guess the audio using the core trivia cogReplaces the core Trivia cog. Needs help adding audio trivia lists, please submit a PR to contribute
| +| ccrole | **Beta** |
Create custom commands that also assign rolesMay have some bugs, please create an issue if you find any
| +| chatter | **Alpha** |
Chat-bot trained to talk like your guildMissing some key features, but currently functional
| +| coglint | **Alpha** |
Error check code in python syntax posted to discordWorks, but probably needs more turning to work for cogs
| +| exclusiverole | **Alpha** |
Prevent certain roles from getting any other rolesFully functional, but pretty simple
| +| fight | **Incomplete** |
Organize bracket tournaments within discordStill in-progress, a massive project
| +| flag | **Alpha** |
Create temporary marks on users that expire after specified timePorted, will not import old data. Please report bugs
| +| forcemention | **Alpha** |
Mentions unmentionable rolesVery simple cog, mention doesn't persist
| +| hangman | **Alpha** |
Play a game of hangmanSome visual glitches and needs more customization
| +| howdoi | **Incomplete** |
Ask coding questions and get results from StackExchangeNot yet functional
| +| leaver | **Alpha** |
Send a message in a channel when a user leaves the serverJust 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 onlineAlpha release, please report bugs
| +| nudity | **Incomplete** |
Checks for NSFW images posted in non-NSFW channelsLibrary 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 serverAlpha release, please report any bugs
| +| reactrestrict | **Alpha** |
Removes reactions by role per channelA 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 discordNo error checking and pretty spammy
| +| scp | **Alpha** |
Look-up SCP articles[Snap-Ons] Just updated to V3
| +| secrethitler | **Incomplete** |
Play the Secret Hitler gameConcept, no work done yet
| +| stealemoji | **Alpha** |
Steals any custom emoji it sees in a reactionSome planned upgrades for server generation
| +| timerole | **Alpha** |
Add roles to members after specified time on the serverUpgraded from V2, please report any bugs
| +| tts | **Beta** |
Send a Text-to-Speech message as an uploaded mp3Alpha 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 discordAnother 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