diff --git a/.gitignore b/.gitignore index cfd538a..6108169 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +Config.json +PrivateConfig.json + # Byte-compiled / optimized / DLL files # Recurse @@ -108,8 +111,10 @@ venv.bak/ # mypy .mypy_cache/ -src/config/Config.json -PrivateConfig.json + # Ignore dockerfiles -docker-compose.yml \ No newline at end of file +docker-compose.yml + +.vscode/ +node_modules/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..95f5a1e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,59 @@ +{ + "name": "sebi-machine", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "discord.js": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.3.2.tgz", + "integrity": "sha512-Abw9CTMX3Jb47IeRffqx2VNSnXl/OsTdQzhvbw/JnqCyqc2imAocc7pX2HoRmgKd8CgSqsjBFBneusz/E16e6A==", + "requires": { + "long": "^4.0.0", + "prism-media": "^0.0.2", + "snekfetch": "^3.6.4", + "tweetnacl": "^1.0.0", + "ws": "^4.0.0" + } + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "prism-media": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.2.tgz", + "integrity": "sha512-L6yc8P5NVG35ivzvfI7bcTYzqFV+K8gTfX9YaJbmIFfMXTs71RMnAupvTQPTCteGsiOy9QcNLkQyWjAafY/hCQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "snekfetch": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz", + "integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw==" + }, + "tweetnacl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz", + "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins=" + }, + "ws": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", + "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a286aaa --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "sebi-machine", + "version": "1.0.0", + "description": "Dedicated Discord bot for [Sebi's bot tutorial](http://discord.gg/GWdhBSp).", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dustinpianalto/Sebi-Machine.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/dustinpianalto/Sebi-Machine/issues" + }, + "homepage": "https://github.com/dustinpianalto/Sebi-Machine#readme", + "dependencies": { + "discord.js": "^11.3.2" + } +} diff --git a/requirements.txt b/requirements.txt index 0927ad0..501e963 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,10 @@ yarl<1.2 numpy==1.14.0 -uvloop +aiofiles +# aiomultiprocess +# aiosqlite +# asyncpg +# dataclasses +# cached_property +uvloop==0.9.1 +aiohttp==3.2.1 \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py index dd592fd..e751564 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -6,7 +6,7 @@ Sebi-Machine. __author__ = 'Annihilator708' # TODO: add yourselves here. I can't remember everyones handles. -__contributors__ = (__author__, 'Neko404NotFound', 'Dusty.P') +__contributors__ = (__author__, 'Neko404NotFound', 'Dusty.P', 'davfsa', 'YashKandalkar') __license__ = 'MIT' __title__ = 'Sebi-Machine' __version__ = 'tbd' diff --git a/src/__main__.py b/src/__main__.py index 824e5ec..a883bd2 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -55,7 +55,7 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): # Load plugins # Add your cog file name in this list - with open(in_here('cogs.txt')) as cog_file: + with open(in_here('extensions.txt')) as cog_file: cogs = cog_file.readlines() for cog in cogs: @@ -63,7 +63,7 @@ class SebiMachine(commands.Bot, LoadConfig, Loggable): cog = cog.replace('\n', '') self.load_extension(f'src.cogs.{cog}') self.logger.info(f'Loaded: {cog}') - + async def on_ready(self): """On ready function""" self.maintenance and self.logger.warning('MAINTENANCE ACTIVE') diff --git a/src/cogs/BasicCommands.py b/src/cogs/BasicCommands.py new file mode 100644 index 0000000..b69c5c5 --- /dev/null +++ b/src/cogs/BasicCommands.py @@ -0,0 +1,56 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from discord.ext import commands +import discord + +class BasicCommands: + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def start(self, ctx): + await ctx.send(f"Hello, {ctx.author.display_name}. Welcome to Sebi's Bot Tutorials. \nFirst off, would you like a quick walkthrough on the server channels?") + + channel_list = {'channel-1' : self.bot.get_channel(333149949883842561).mention, + 'd.py-rewrite-start' : self.bot.get_channel(386419285439938560).mention, + 'js-klasa-start' : self.bot.get_channel(341816240186064897).mention, + 'd.js' : self.bot.get_channel(436771798303113217).mention} + + bots_channels = (self.bot.get_channel(339112602867204097).mention, + self.bot.get_channel(411586546551095296).mention) + + help_channels = (self.bot.get_channel(425315253153300488).mention, + self.bot.get_channel(392215236612194305).mention, + self.bot.get_channel(351034776985141250).mention) + + def check(m): + return True if m.author.id == ctx.author.id and m.channel.id == ctx.channel.id else False + + msg = await self.bot.wait_for('message', check = check, timeout = 15) + + agree = ("yes", "yep", "yesn't", "ya") + + if msg is None: + await ctx.send("Sorry, {ctx.author.mention}, you didn't reply on time. You can run the command again when you're free :)") + else: + if msg.content.lower() in agree: + async with ctx.typing(): + await ctx.send("Alrighty-Roo...") + await ctx.send(f"""To start making your bot from scratch, you first need to head over to {channel_list['channel-1']} + (Regardless of the language you're gonna use).""") + await ctx.send(f"""After you have a bot account, you can either continue with {channel_list['d.py-rewrite-start']} + if you want to make a bot in discord.py rewrite __or__ go to {channel_list['js-klasa-start']} or + {channel_list['d.js']} for making a bot in JavaScript""") + await ctx.send("...Read all the tutorials and still need help? You have two ways to get help.") + await ctx.send(f"""**Method-1**\nThis is the best method of getting help. You help yourself.\n + To do so, head over to a bots dedicated channel (either {bots_channels[0]} or {bots_channels[1]}) + and type `?rtfm rewrite thing_you_want_help_with`.\nThis will trigger the bot R.Danny Bot and will + give you links on your query on the official discord.py rewrite docs. *PS: Let the page completely load*""") + await ctx.send(f"""**Method-2**\nIf you haven't found anything useful with Method-1, feel free to ask your question + in any of the related help channels. ({', '.join(help_channels)})\nMay the force be with you!!""") + else: + return await ctx.send("Session terminated. You can run this command again whenever you want.") + +def setup(bot): + bot.add_cog(BasicCommands(bot)) \ No newline at end of file diff --git a/src/cogs/contributors.py b/src/cogs/contributors.py index f942184..159b8b6 100644 --- a/src/cogs/contributors.py +++ b/src/cogs/contributors.py @@ -1,9 +1,10 @@ #!/usr/bin/python -# -*- coding: -*- +# -*- coding: utf-8 -*- from discord.ext import commands import discord import traceback +import aiofiles class Upload: """ @@ -79,30 +80,39 @@ class Upload: await ctx.send(f'Could not load `{extension}` -> `{e}`') else: await ctx.send(f'Loaded `{extension}`.') - + @commands.command() - async def kick(self, ctx, member: discord.Member = None): - """ - Kick a discord member from your server. - Only contributors can use this command - - Usage: - - kick - - """ + async def permunload(self, ctx, extension=None): + """Disables permanently a cog.""" await ctx.trigger_typing() if ctx.author.id not in self.bot.ownerlist: return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10) - - if member is None: - await ctx.send('Are you sure you are capable of this command?') - try: - await member.kick() - await ctx.send(f'You kicked **`{member.name}`** from **`{ctx.guild.name}`**') - - except Exception as e: - await ctx.send('You may not use this command you do not have permission in server:\n\n**`{ctx.guild.name}`**' - f'\n\n```py\n{e}\n```') + + if cog is None: + return await ctx.send("Please provide a extension. Do `help permunload` for more info") + + extension = extension.lower() + + async with aiofiles.open("extension.txt") as fp: + lines=fp.readlines() + + removed = False + async with aiofiles.open("extension.txt", "w") as fp: + for i in lines: + if i.replace("\n", "") != extension: + fp.write(i) + else: + removed = True + break + + if removed is True: + try: + self.bot.unload_extension(extension) + except: + pass + return await ctx.send("Extension removed successfully") + + await ctx.send("Extension not found") def setup(bot): - bot.add_cog(Upload(bot)) \ No newline at end of file + bot.add_cog(Upload(bot)) diff --git a/src/cogs/example.py b/src/cogs/example.py index 4a73cbc..0376923 100644 --- a/src/cogs/example.py +++ b/src/cogs/example.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# -*- coding: -*- +# -*- coding: utf-8 -*- from discord.ext import commands import discord diff --git a/src/cogs/fun.py b/src/cogs/fun.py index d066435..ec56b83 100644 --- a/src/cogs/fun.py +++ b/src/cogs/fun.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# -*- coding: -*- +# -*- coding: utf-8 -*- from discord.ext import commands import discord diff --git a/src/cogs/git.py b/src/cogs/git.py index 06645f6..2be5354 100644 --- a/src/cogs/git.py +++ b/src/cogs/git.py @@ -41,7 +41,7 @@ class Git(Loggable): @commands.group(case_insensitive=True, invoke_without_command=True) async def git(self, ctx): """Run help git for more info""" - await ctx.send('https://github.com/Annihilator708/Sebi-Machine/') + await ctx.send('https://github.com/dustinpianalto/Sebi-Machine/') @commands.command(case_insensitive=True, brief='Gets the Trello link.') async def trello(self, ctx): diff --git a/src/cogs/moderation.py b/src/cogs/moderation.py new file mode 100644 index 0000000..df0fb22 --- /dev/null +++ b/src/cogs/moderation.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from discord.ext import commands +import discord + +class Moderation: + """ + Moderation Commands + """ + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def sar(self, ctx): + """Assign or remove self assigned roles.""" + pass + + @commands.command() + async def kick(self, ctx, member: discord.Member = None): + """ + Kick a discord member from your server. + Only contributors can use this command. + + Usage: + - kick + + """ + await ctx.trigger_typing() + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10) + + if member is None: + await ctx.send('Are you sure you are capable of this command?') + try: + await member.kick() + await ctx.send(f'You kicked **`{member.name}`** from **`{ctx.guild.name}`**') + + except Exception as e: + await ctx.send('You may not use this command you do not have permission in server:\n\n**`{ctx.guild.name}`**' + f'\n\n```py\n{e}\n```') + +def setup(bot): + bot.add_cog(Moderation(bot)) diff --git a/src/cogs/sar.js b/src/cogs/sar.js new file mode 100644 index 0000000..7cc25d4 --- /dev/null +++ b/src/cogs/sar.js @@ -0,0 +1,67 @@ +const Discord = require("discord.js"); + +exports.run = async function(client, message, args) { + + /* + aliases: sar, selfrole, selfroles + + examples: + - S!selfrole get 1 (adds heroku helper role) + - S!sar remove 3 (removes rewrite helper role) + - S!sar list (shows all roles) + */ + + function roleFinder(query) { + return message.guild.roles.find(function(r) { + return r.name.includes(query) + }).id; + } + + const type = args[0]; // can be get, remove or list + + if (type == "list" || type == undefined) { + + const embed = new Discord.RichEmbed() + .setTitle("List of Self Assigned Roles") + .setDescription("Usage: `S!sar [ get | remove | list ] [ number ]`") + .addField("1. Heroku Helper", "S!sar get 1", true) + .addField("2. JS Helper", "S!sar get 2", true) + .addField("3. Rewrite Helper", "S!sar get 3", true) + .setColor("AQUA"); + + return message.channel.send({ + embed: embed + }); + + } + + const roles = [roleFinder("Heroku"), roleFinder("JS"), roleFinder("Rewrite")]; + + let choice = args[1]; // can be 1, 2 or 3 + + // if the choice is not 1, 2 or 3 + if (/^[123]$/.test(choice) == false) { + return message.channel.send("Enter a valid role number!"); // returns error message + } else { + choice -= 1; // because array indexing starts from 0. when they choose 1 it should be roles[0] + } + + switch (type) { + + case "get": + message.member.addRole(roles[choice]); + message.channel.send("Added the role you specified!"); // confirmation message + break; + + case "remove": + message.member.removeRole(roles[choice]); + message.channel.send("Removed the role you specified!"); // confirmation message + break; + + default: + return; // when it is neither get nor remove + break; + + } + +} \ No newline at end of file diff --git a/src/cogs/tag.py b/src/cogs/tag.py new file mode 100644 index 0000000..3bae890 --- /dev/null +++ b/src/cogs/tag.py @@ -0,0 +1,97 @@ +import discord +from discord.ext import commands +import json +import aiofiles +import asyncio + +class Tag: + def __init__(self, bot): + self.bot = bot + with open("src/shared_libs/tags.json", "r") as fp: + json_data = fp.read() + global tags + tags = json.loads(json_data) + + @commands.group(case_insensitive=True, invoke_without_command=True) + async def tag(self, ctx, tag=None): + """Gets a tag""" + await ctx.trigger_typing() + if tag is None: + return await ctx.send('Please provide a argument. Do `help tag` for more info') + + found = tags.get(tag, None) + + if found is None: + return await ctx.send('Tag not found') + + await ctx.send(found) + + @tag.command(case_insensitive=True) + async def list(self, ctx): + """Lists available tags""" + await ctx.trigger_typing() + desc = "" + for i in tags: + desc = desc + i + "\n" + + if desc == "": + desc = "None" + + em = discord.Embed(title='Available tags:', description=desc ,colour=discord.Colour(0x00FFFF)) + + await ctx.send(embed=em) + + @tag.command(case_insensitive=True) + async def add(self, ctx, tag_name=None, *, tag_info=None): + """Adds a new tag""" + await ctx.trigger_typing() + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send("You are not allowed to do this") + + if tag_name is None or tag_info is None: + return await ctx.send("Please provide a tag name and the tag info. Do `help tag` for more info") + + exists = False + for i in tags: + if i == tag_name: + exists = True + + if not exists: + tags.update({tag_name : tag_info}) + + async with aiofiles.open("src/shared_libs/tags.json", "w") as fp: + json_data = json.dumps(tags) + await fp.write(json_data) + + return await ctx.send("The tag has been added") + + await ctx.send("The tag already exists") + + @tag.command(case_insensitive=True) + async def remove(self, ctx, tag=None): + """Remove a existing tag""" + await ctx.trigger_typing() + if not ctx.author.guild_permissions.manage_guild: + return await ctx.send("You are not allowed to do this") + + if tag is None: + return await ctx.send("Please provide a tag name and the tag info. Do `help tag` for more info") + + found = None + for i in tags: + if i == tag: + found = i + + if found is not None: + del tags[found] + async with aiofiles.open("src/shared_libs/tags.json", "w") as fp: + json_data = json.dumps(tags) + await fp.write(json_data) + + return await ctx.send("The tag has been removed") + + await ctx.send("The tag has not been found") + + +def setup(bot): + bot.add_cog(Tag(bot)) diff --git a/src/config/Config.json b/src/config/Config.json deleted file mode 100644 index a301982..0000000 --- a/src/config/Config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "version": 0.1, - "display_name" : "[ds!] Dev-Sebi", - "maintenance": "True", - "ownerlist": [387871282756190208, 275280442884751360, 351794468870946827, 140652945032216576], - "prefix": "ds!", - "dbconnect": "" -} diff --git a/src/config/PrivateConfig.json b/src/config/PrivateConfig.json deleted file mode 100644 index 9fa108b..0000000 --- a/src/config/PrivateConfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "bot-key": "" -} \ No newline at end of file diff --git a/src/cogs.txt b/src/extensions.txt similarity index 53% rename from src/cogs.txt rename to src/extensions.txt index cbb47e8..a47049f 100644 --- a/src/cogs.txt +++ b/src/extensions.txt @@ -1,5 +1,8 @@ example contributors +tag code git fun +moderation +BasicCommands diff --git a/src/run.js b/src/run.js new file mode 100644 index 0000000..d949397 --- /dev/null +++ b/src/run.js @@ -0,0 +1,47 @@ +const Discord = require("discord.js"); +const client = new Discord.Client(); + +const config = require("./config/Config.json"); +const prefix = config.prefix; + +const aliases = require("./shared_libs/aliases.json"); +const privateConfig = require("./config/PrivateConfig.json"); + +const fs = require("fs"); +const commands = fs.readdirSync("./cogs").filter(function(e) { + return e.endsWith(".js"); +}); + + +client.on("message", function(message) { + + if (message.guild.id != "265828729970753537") { + return; + } + + if (message.content.startsWith(config.prefix) == false) { + return; + } + + const msg = message.content.replace(prefix, ""); + + let command = msg.split(" ")[0]; + const args = msg.split(" ").slice(1); + + command = aliases[command]; + + try { + + if (commands.includes(`${command}.js`)) { + require(`./cogs/${command}.js`).run(client, message, args); + } + + } catch (err) { + + // handling errors + + } + +}); + +client.login(privateConfig["bot-key"]); \ No newline at end of file diff --git a/src/shared_libs/tags.json b/src/shared_libs/tags.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/shared_libs/tags.json @@ -0,0 +1 @@ +{} \ No newline at end of file