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 0bb7fc2..7c516ae 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,25 @@ Cog Function | 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
| | 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
| | fight | **Incomplete** |
Organize bracket tournaments within discordStill in-progress, a massive project
| -| flag | **Incomplete** |
Create temporary marks on users that expire after specified timeNot yet ported to v3
| +| 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** |
Create temporary marks on users that expire after specified timeNot yet ported to v3
| -| leaver | **Incomplete** |
Send a message in a channel when a user leaves the serverNot yet ported to v3
| +| 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
| | lseen | **Alpha** |
Track when a member was last onlineAlpha release, please report bugs
| | reactrestrict | **Alpha** |
Removes reactions by role per channelA bit clunky, but functional
| | sayurl | **Alpha** |
Convert any URL into text and post to discordNo error checking and pretty spammy
| | 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 | **Alpha** |
Send a Text-to-Speech message as an uploaded mp3Alpha release, please report any bugs
| +| qrinvite | **Alpha** |
Create a QR code invite for the serverAlpha release, please report any bugs
| | werewolf | **Alpha** |
Play the classic party game Werewolf within discordAnother massive project currently being developed, will be fully customizable
| 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..17a1bd7 --- /dev/null +++ b/announcedaily/announcedaily.py @@ -0,0 +1,250 @@ +import asyncio +import random +from datetime import datetime, timedelta + +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" +] + + +class AnnounceDaily: + """ + 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..c2e9ce6 --- /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]help AnnounceDaily`", + "requirements": [], + "short": "Send daily announcements", + "tags": [ + "bobloy" + ] +} \ No newline at end of file diff --git a/ccrole/ccrole.py b/ccrole/ccrole.py index d8c17f6..a92ef2d 100644 --- a/ccrole/ccrole.py +++ b/ccrole/ccrole.py @@ -2,7 +2,6 @@ import asyncio import re import discord - from redbot.core import Config, checks from redbot.core import commands from redbot.core.utils.chat_formatting import pagify, box @@ -30,7 +29,7 @@ class CCRole: 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) @@ -106,7 +105,7 @@ class CCRole: 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) @@ -191,7 +190,7 @@ class CCRole: """Shows custom commands list""" guild = ctx.guild cmd_list = await self.config.guild(guild).cmdlist() - cmd_list = {k: v for k,v in cmd_list.items() if v} + 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( diff --git a/ccrole/info.json b/ccrole/info.json index 73a1f79..addc00f 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.", + "requirements": [], + "short": "[Incomplete] Creates commands that adjust roles", + "tags": [ + "fox", + "bobloy", + "utility", + "tools", + "roles" + ] } \ No newline at end of file diff --git a/chatter/chat.py b/chatter/chat.py index dce136f..0cbd9c4 100644 --- a/chatter/chat.py +++ b/chatter/chat.py @@ -1,16 +1,18 @@ 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 - class Chatter: """ This cog trains a chatbot that will talk like members of your Guild @@ -24,11 +26,23 @@ class Chatter: "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='./database.sqlite3' + 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) @@ -43,21 +57,42 @@ class Chatter: Currently takes a stupid long time Returns a list of text """ - out = [] + 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[-1] += "\n" + message.clean_content + out[i][-1] += "\n" + message.clean_content else: user = message.author - out.append(message.clean_content) + out[i].append(message.clean_content) + except discord.Forbidden: pass except discord.HTTPException: @@ -70,18 +105,19 @@ class Chatter: def _train(self, data): try: - self.chatbot.train(data) + for convo in data: + self.chatbot.train(convo) except: return False return True - @commands.group() + @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: - await ctx.send_help() + pass @chatter.command() async def age(self, ctx: commands.Context, days: int): @@ -99,7 +135,8 @@ class Chatter: 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)) + future = await self.loop.run_in_executor(None, self.chatbot.trainer.export_for_training, + './{}.json'.format(backupname)) if future: await ctx.send("Backup successful!") @@ -134,17 +171,21 @@ class Chatter: else: await ctx.send("Error occurred :(") - async def on_message(self, message): + 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 - channel = message.channel + try: + guild: discord.Guild = message.guild + except AttributeError: # Not a guild message + return + channel: discord.TextChannel = message.channel - if message.author.id != self.bot.user.id: - to_strip = "@" + author.guild.me.display_name + " " + if author.id != self.bot.user.id: + to_strip = "@" + guild.me.display_name + " " text = message.clean_content if not text.startswith(to_strip): return @@ -152,7 +193,7 @@ class Chatter: async with channel.typing(): future = await self.loop.run_in_executor(None, self.chatbot.get_response, text) - if future: + if future and str(future): await channel.send(str(future)) else: await channel.send(':thinking:') diff --git a/chatter/chatterbot/chatterbot.py b/chatter/chatterbot/chatterbot.py index c7a92cb..08576c3 100644 --- a/chatter/chatterbot/chatterbot.py +++ b/chatter/chatterbot/chatterbot.py @@ -2,10 +2,7 @@ from __future__ import unicode_literals import logging -from . import utils -from .input import InputAdapter -from .output import OutputAdapter -from .storage import StorageAdapter +from chatter.chatterbot import utils class ChatBot(object): @@ -14,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 @@ -33,9 +30,9 @@ class ChatBot(object): 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) @@ -139,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/chatterbot/comparisons.py b/chatter/chatterbot/comparisons.py index 59efa95..5e253a0 100644 --- a/chatter/chatterbot/comparisons.py +++ b/chatter/chatterbot/comparisons.py @@ -92,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') @@ -100,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') @@ -108,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') @@ -177,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') @@ -252,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/chatterbot/logic/__init__.py b/chatter/chatterbot/logic/__init__.py index 1930556..8a6cc97 100644 --- a/chatter/chatterbot/logic/__init__.py +++ b/chatter/chatterbot/logic/__init__.py @@ -1,5 +1,5 @@ -from .best_match import BestMatch from .logic_adapter import LogicAdapter +from .best_match import BestMatch from .low_confidence import LowConfidenceAdapter from .mathematical_evaluation import MathematicalEvaluation from .multi_adapter import MultiLogicAdapter diff --git a/chatter/chatterbot/logic/best_match.py b/chatter/chatterbot/logic/best_match.py index 5c48121..f19fc99 100644 --- a/chatter/chatterbot/logic/best_match.py +++ b/chatter/chatterbot/logic/best_match.py @@ -1,6 +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/chatterbot/logic/low_confidence.py b/chatter/chatterbot/logic/low_confidence.py index 585cf20..2d33bba 100644 --- a/chatter/chatterbot/logic/low_confidence.py +++ b/chatter/chatterbot/logic/low_confidence.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from chatter.chatterbot.conversation import Statement -from .best_match import BestMatch +from chatter.chatterbot.logic import BestMatch class LowConfidenceAdapter(BestMatch): diff --git a/chatter/chatterbot/logic/multi_adapter.py b/chatter/chatterbot/logic/multi_adapter.py index 6cfe30f..5ae79f4 100644 --- a/chatter/chatterbot/logic/multi_adapter.py +++ b/chatter/chatterbot/logic/multi_adapter.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from collections import Counter from chatter.chatterbot import utils -from .logic_adapter import LogicAdapter +from chatter.chatterbot.logic import LogicAdapter class MultiLogicAdapter(LogicAdapter): diff --git a/chatter/chatterbot/logic/no_knowledge_adapter.py b/chatter/chatterbot/logic/no_knowledge_adapter.py index 55208b4..848b23e 100644 --- a/chatter/chatterbot/logic/no_knowledge_adapter.py +++ b/chatter/chatterbot/logic/no_knowledge_adapter.py @@ -1,6 +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/chatterbot/logic/specific_response.py b/chatter/chatterbot/logic/specific_response.py index 101dd3b..ef7a630 100644 --- a/chatter/chatterbot/logic/specific_response.py +++ b/chatter/chatterbot/logic/specific_response.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from .logic_adapter import LogicAdapter +from chatter.chatterbot.logic import LogicAdapter class SpecificResponseAdapter(LogicAdapter): diff --git a/chatter/chatterbot/logic/time_adapter.py b/chatter/chatterbot/logic/time_adapter.py index 72902e2..d4bbd15 100644 --- a/chatter/chatterbot/logic/time_adapter.py +++ b/chatter/chatterbot/logic/time_adapter.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from datetime import datetime -from .logic_adapter import LogicAdapter +from chatter.chatterbot.logic import LogicAdapter class TimeLogicAdapter(LogicAdapter): diff --git a/chatter/chatterbot/output/__init__.py b/chatter/chatterbot/output/__init__.py index 80abe4f..52c3534 100644 --- a/chatter/chatterbot/output/__init__.py +++ b/chatter/chatterbot/output/__init__.py @@ -1,8 +1,8 @@ +from .output_adapter import OutputAdapter from .gitter import Gitter from .hipchat import HipChat from .mailgun import Mailgun from .microsoft import Microsoft -from .output_adapter import OutputAdapter from .terminal import TerminalAdapter __all__ = ( diff --git a/chatter/chatterbot/output/gitter.py b/chatter/chatterbot/output/gitter.py index ba01fa8..664d341 100644 --- a/chatter/chatterbot/output/gitter.py +++ b/chatter/chatterbot/output/gitter.py @@ -1,6 +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/chatterbot/output/hipchat.py b/chatter/chatterbot/output/hipchat.py index 2546092..20029fa 100644 --- a/chatter/chatterbot/output/hipchat.py +++ b/chatter/chatterbot/output/hipchat.py @@ -2,7 +2,7 @@ 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/chatterbot/output/mailgun.py b/chatter/chatterbot/output/mailgun.py index 71a9a7a..d022a51 100644 --- a/chatter/chatterbot/output/mailgun.py +++ b/chatter/chatterbot/output/mailgun.py @@ -1,6 +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/chatterbot/output/microsoft.py b/chatter/chatterbot/output/microsoft.py index 816fc97..4f2426a 100644 --- a/chatter/chatterbot/output/microsoft.py +++ b/chatter/chatterbot/output/microsoft.py @@ -2,7 +2,7 @@ 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/chatterbot/output/terminal.py b/chatter/chatterbot/output/terminal.py index 8ab63e1..005d0ae 100644 --- a/chatter/chatterbot/output/terminal.py +++ b/chatter/chatterbot/output/terminal.py @@ -1,6 +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/chatterbot/storage/storage_adapter.py b/chatter/chatterbot/storage/storage_adapter.py index 046ae63..cf1f45b 100644 --- a/chatter/chatterbot/storage/storage_adapter.py +++ b/chatter/chatterbot/storage/storage_adapter.py @@ -158,7 +158,9 @@ 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.'): + 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/chatterbot/trainers.py b/chatter/chatterbot/trainers.py index 042019e..f3a4165 100644 --- a/chatter/chatterbot/trainers.py +++ b/chatter/chatterbot/trainers.py @@ -2,8 +2,8 @@ import logging import os import sys -from . import utils -from .conversation import Statement, Response +from chatter.chatterbot import utils +from chatter.chatterbot.conversation import Statement, Response class Trainer(object): @@ -127,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() diff --git a/chatter/chatterbot/utils.py b/chatter/chatterbot/utils.py index e18549e..9785bd4 100644 --- a/chatter/chatterbot/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): diff --git a/coglint/coglint.py b/coglint/coglint.py index 10861c7..0c3d045 100644 --- a/coglint/coglint.py +++ b/coglint/coglint.py @@ -1,11 +1,8 @@ import discord - -from redbot.core import Config, checks - -from redbot.core.bot import Red - 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 diff --git a/fight/fight.py b/fight/fight.py index b050de8..efba2ed 100644 --- a/fight/fight.py +++ b/fight/fight.py @@ -1,10 +1,11 @@ +import asyncio import os import math # from typing import Union import discord - +from redbot.core import commands from redbot.core.utils.chat_formatting import pagify from redbot.core.utils.chat_formatting import box @@ -102,7 +103,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") @@ -198,10 +199,10 @@ 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, mID, score1, score2): + async def fadmin_score(self, ctx: commands.Context, mID, score1, score2): """Set's the score for matchID and clears disputes""" currFight = await self._getcurrentfight(ctx) tID = await self._activefight(ctx) @@ -213,11 +214,11 @@ class Fight: await ctx.send("Tournament currently not accepting new players") return - if await self._infight(ctx, tID, user.id): + if await self._infight(ctx, tID, ctx.user.id): await ctx.send("You are already in this tournament!") return - currFight["PLAYERS"].append(user.id) + currFight["PLAYERS"].append(ctx.user.id) await self._save_fight(ctx, tID, currFight) @@ -256,7 +257,7 @@ class Fight: # self.save_data() if ctx.invoked_subcommand is None: - await ctx.send_help() + pass # await ctx.send("I can do stuff!") @fightset.command(name="emoji") @@ -548,7 +549,7 @@ 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): @@ -711,11 +712,13 @@ class Fight: async def _embed_tourney(self, ctx, tID): """Prints a pretty embed of the tournament""" - await ctx.send("_placeholder Todo") + #_placeholder Todo + pass async def _comparescores(self): """Checks user submitted scores for inconsistancies""" - await ctx.send("_comparescores Todo") + # _comparescores Todo + pass async def _parseuser(self, guild: discord.Guild, tID, userid): """Finds user in the tournament""" @@ -821,8 +824,8 @@ class Fight: """Reports a win for member in match""" theT = await self._getfight(guild, tID) - if member.id not in theT["PLAYERS"]: # Shouldn't happen - return False + # if member.id not in theT["PLAYERS"]: # Shouldn't happen + # return False if theT["RULES"]["TYPE"] == 0: return await self._rr_report_dispute(guild, tID, mID) @@ -833,13 +836,16 @@ class Fight: # **********************Single Elimination*************************** async def _elim_setup(self, tID): - await ctx.send("Elim setup todo") + # ToDo Elim setup + pass async def _elim_start(self, tID): - await ctx.send("Elim start todo") + # ToDo Elim start + pass async def _elim_update(self, matchID): - await ctx.send("Elim update todo") + # ToDo Elim update + pass # **********************Round-Robin********************************** diff --git a/fight/info.json b/fight/info.json index 8805dad..fabe8ca 100644 --- a/fight/info.json +++ b/fight/info.json @@ -2,7 +2,7 @@ "author" : ["Bobloy"], "bot_version" : [3,0,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..1ececf4 --- /dev/null +++ b/flag/flag.py @@ -0,0 +1,184 @@ +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 + + +class Flag: + """ + 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.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 + } + + # ************************Flag command group start************************ + @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..") + + @checks.mod_or_permissions(manage_roles=True) + @commands.command(pass_context=True, no_pm=True, 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.command(pass_context=True, no_pm=True, 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.command(pass_context=True, no_pm=True, 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..b5908b9 --- /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": true, + "install_msg": "Thank you for installing Flag! Get started with `[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..8086d7d --- /dev/null +++ b/forcemention/forcemention.py @@ -0,0 +1,38 @@ +from discord.utils import get + +from redbot.core import Config, checks, commands + +from redbot.core.bot import Red + + +class ForceMention: + """ + 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..46810ae --- /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]forcemention`", + "requirements": [], + "short": "Mention unmentionables", + "tags": [ + "bobloy", + "utils" + ] +} \ No newline at end of file diff --git a/hangman/hangman.py b/hangman/hangman.py index 6e5dba6..7002e8f 100644 --- a/hangman/hangman.py +++ b/hangman/hangman.py @@ -127,7 +127,7 @@ class Hangman: @checks.mod_or_permissions(administrator=True) async def hangset(self, ctx): """Adjust hangman settings""" - if not ctx.invoked_subcommand: + if ctx.invoked_subcommand is None: pass @hangset.command(pass_context=True) diff --git a/howdoi/howdoi.py b/howdoi/howdoi.py index 019c014..17fc623 100644 --- a/howdoi/howdoi.py +++ b/howdoi/howdoi.py @@ -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/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..08bef6f 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]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 2aff2ac..a9b4a6e 100644 --- a/leaver/leaver.py +++ b/leaver/leaver.py @@ -1,78 +1,42 @@ import discord -import os -from datetime import datetime - -from .utils.dataIO import dataIO -from .utils import checks +from redbot.core import Config, checks, commands +from redbot.core.commands import Context class Leaver: - """Creates a goodbye message when people leave""" + """ + 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/lseen/info..json b/lseen/info..json new file mode 100644 index 0000000..9f69325 --- /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": true, + "install_msg": "Thank you for installing LastSeen. Get started with `[p]help LastSeen`", + "requirements": [], + "short": "Last seen tracker", + "tags": [ + "bobloy", + "utils", + "tools" + ] +} \ No newline at end of file diff --git a/lseen/lseen.py b/lseen/lseen.py index 43c56ea..7693385 100644 --- a/lseen/lseen.py +++ b/lseen/lseen.py @@ -10,7 +10,7 @@ from redbot.core import commands class LastSeen: """ - V3 Cog Template + Report when a user was last seen online """ online_status = discord.Status.online @@ -41,7 +41,7 @@ class LastSeen: async def lset(self, ctx: commands.Context): """Change settings for lseen""" if ctx.invoked_subcommand is None: - await ctx.send_help() + pass @lset.command(name="toggle") async def lset_toggle(self, ctx: commands.Context): @@ -58,8 +58,6 @@ class LastSeen: async def lseen(self, ctx: commands.Context, member: discord.Member): """ Just says the time the user was last seen - - :param member: """ if member.status != self.offline_status: 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..4384930 --- /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]help LastSeen`", + "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/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..3015652 --- /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": true, + "install_msg": "Thank you for installing QRInvite! Get started with `[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..4180bb4 --- /dev/null +++ b/qrinvite/qrinvite.py @@ -0,0 +1,81 @@ +import pathlib + +import aiohttp +import discord +from PIL import Image + +from redbot.core import Config, checks, commands + +from redbot.core.bot import Red + +from MyQR import myqr +from redbot.core.data_manager import cog_data_path + + +class QRInvite: + """ + 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 + + 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) + newPath = path.replace(".webp",".png") + im.save(newPath, transparency=255) + return newPath \ No newline at end of file diff --git a/reactrestrict/reactrestrict.py b/reactrestrict/reactrestrict.py index 87b50a3..f1bb2c3 100644 --- a/reactrestrict/reactrestrict.py +++ b/reactrestrict/reactrestrict.py @@ -1,12 +1,9 @@ -import asyncio from typing import List, Union import discord - - from redbot.core import Config -from redbot.core.bot import Red from redbot.core import commands +from redbot.core.bot import Red class ReactRestrictCombo: @@ -16,8 +13,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): @@ -83,7 +80,7 @@ class ReactRestrict: """ # is_custom = True # if isinstance(emoji, str): - # is_custom = False + # is_custom = False combo = ReactRestrictCombo(message_id, role.id) @@ -95,10 +92,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() @@ -109,14 +106,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): @@ -169,8 +165,8 @@ 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. @@ -180,12 +176,12 @@ 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 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. @@ -199,12 +195,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 @@ -214,7 +208,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): @@ -228,18 +222,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) @@ -251,10 +245,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) @@ -298,50 +292,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/sayurl/sayurl.py b/sayurl/sayurl.py index c536b41..04499cd 100644 --- a/sayurl/sayurl.py +++ b/sayurl/sayurl.py @@ -1,7 +1,7 @@ import aiohttp import html2text -from redbot.core import Config +from redbot.core import Config, commands from redbot.core.bot import Red from redbot.core.utils.chat_formatting import pagify @@ -32,8 +32,7 @@ class SayUrl: """ Converts a URL to something readable - :param url: - :return: + Works better on smaller websites """ h = html2text.HTML2Text() diff --git a/secrethitler/secrethitler.py b/secrethitler/secrethitler.py index edee4d0..2f9360b 100644 --- a/secrethitler/secrethitler.py +++ b/secrethitler/secrethitler.py @@ -3,7 +3,7 @@ import asyncio import discord -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/stealemoji.py b/stealemoji/stealemoji.py index a55d2c9..05dd961 100644 --- a/stealemoji/stealemoji.py +++ b/stealemoji/stealemoji.py @@ -45,7 +45,7 @@ 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): @@ -58,7 +58,7 @@ class StealEmoji: 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 diff --git a/timerole/timerole.py b/timerole/timerole.py index fb2fff2..25a7c1b 100644 --- a/timerole/timerole.py +++ b/timerole/timerole.py @@ -37,7 +37,7 @@ class Timerole: async def timerole(self, ctx): """Adjust timerole settings""" if ctx.invoked_subcommand is None: - await ctx.send_help() + pass @timerole.command() async def addrole(self, ctx: commands.Context, role: discord.Role, days: int, *requiredroles: discord.Role): 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..babe7fc --- /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]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..dcae0be --- /dev/null +++ b/tts/tts.py @@ -0,0 +1,34 @@ +import io + +import discord +from gtts import gTTS +from redbot.core import Config, commands +from redbot.core.bot import Red + + +class TTS: + """ + 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(aliases=["t2s", "text2"]) + async def tts(self, ctx: commands.Context, *, text: str): + """ + My custom cog + + Extra information goes here + """ + 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/werewolf/builder.py b/werewolf/builder.py index 28b22ea..48e7e71 100644 --- a/werewolf/builder.py +++ b/werewolf/builder.py @@ -120,8 +120,6 @@ async def parse_code(code, game): digits += 1 continue - - try: idx = int(built) except ValueError: @@ -146,7 +144,6 @@ async def parse_code(code, game): built = "" - return decode diff --git a/werewolf/player.py b/werewolf/player.py index d1f9359..c84d87f 100644 --- a/werewolf/player.py +++ b/werewolf/player.py @@ -30,4 +30,4 @@ class Player: 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)) \ No newline at end of file + await self.role.game.village_channel.send("Couldn't DM {}, uh oh".format(self.mention)) diff --git a/werewolf/roles/seer.py b/werewolf/roles/seer.py index b005b9a..5c58250 100644 --- a/werewolf/roles/seer.py +++ b/werewolf/roles/seer.py @@ -16,7 +16,6 @@ class Seer(Role): 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) # self.game = game diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py index e312312..59c18c6 100644 --- a/werewolf/werewolf.py +++ b/werewolf/werewolf.py @@ -53,7 +53,7 @@ class Werewolf: 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") @@ -136,7 +136,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 @commands.guild_only() @ww.command(name="new")