[V3 Everything] Package bot and write setup scripts (#964)

Ya'll are gonna hate me.

* Initial modifications

* Add initial setup.py

* working setup py help

* Modify setup file to package stuff

* Move a bunch of shit and fix imports

* Fix or skip tests

* Must add init files for find_packages to work

* Move main to scripts folder and rename

* Add shebangs

* Copy over translation files

* WORKING PIP INSTALL

* add dependency information

* Hardcoded version for now, will need to figure out a better way to do this

* OKAY ITS FINALLY FUCKING WORKING

* Add this guy

* Fix stuff

* Change readme to rst

* Remove double sentry opt in

* Oopsie

* Fix this thing

* Aaaand fix test

* Aaaand fix test

* Fix core cog importing and default cog install path

* Adjust readme

* change instance name from optional to required

* Ayyy let's do more dependency injection
This commit is contained in:
Will 2017-09-08 23:14:32 -04:00 committed by GitHub
parent 6b1fc786ee
commit d69fd63da7
85 changed files with 451 additions and 255 deletions

View File

@ -4,9 +4,9 @@ python:
- "3.5.3" - "3.5.3"
- "3.6.1" - "3.6.1"
install: install:
- pip install -r requirements.txt - pip install --process-dependency-links -e .[test]
script: script:
- python -m compileall ./cogs - python -m compileall ./redbot/cogs
- python -m pytest - python -m pytest
cache: pip cache: pip
notifications: notifications:

4
MANIFEST.in Normal file
View File

@ -0,0 +1,4 @@
include README.rst
include LICENSE
include requirements.txt
include discord/bin/*.dll

View File

@ -4,3 +4,11 @@
**This is alpha and very much a work in progress. Regular use is not recommended. **This is alpha and very much a work in progress. Regular use is not recommended.
There will not be any effort not to break current installations.** There will not be any effort not to break current installations.**
### How to install
Using python3 pip::
pip install --process-dependency-links -U git+https://github.com/Cog-Creators/Red-DiscordBot@V3/develop
redbot-setup
redbot <name>

View File

@ -1,40 +0,0 @@
from core.config import Config
from subprocess import run, PIPE
from collections import namedtuple
from main import determine_main_folder
__all__ = ["Config", "__version__"]
version_info = namedtuple("VersionInfo", "major minor patch")
BASE_VERSION = version_info(3, 0, 0)
def get_latest_version():
main_folder = determine_main_folder()
try:
p = run(
"git describe --abbrev=0 --tags".split(),
stdout=PIPE,
cwd=str(main_folder)
)
except FileNotFoundError:
# No git
return BASE_VERSION
if p.returncode != 0:
return BASE_VERSION
stdout = p.stdout.strip().decode()
if stdout.startswith("v"):
numbers = stdout[1:].split('.')
args = [0, 0, 0]
for i in range(3):
try:
args[i] = int(numbers[i])
except (IndexError, ValueError):
args[i] = 0
return version_info(*args)
return BASE_VERSION
__version__ = get_latest_version()

View File

@ -1,29 +1,31 @@
#!/usr/bin/env python
# Discord Version check # Discord Version check
import discord
import sys import sys
import discord
if discord.version_info.major < 1: if discord.version_info.major < 1:
print("You are not running the rewritten version of discord.py.\n\n" print("You are not running the rewritten version of discord.py.\n\n"
"In order to use Red v3 you MUST be running d.py version" "In order to use Red v3 you MUST be running d.py version"
" >= 1.0.0.") " >= 1.0.0.")
sys.exit(1) sys.exit(1)
from core.bot import Red, ExitCodes from redbot.core.bot import Red, ExitCodes
from core.cog_manager import CogManagerUI from redbot.core.cog_manager import CogManagerUI
from core.data_manager import load_basic_configuration from redbot.core.data_manager import load_basic_configuration
from core.global_checks import init_global_checks from redbot.core.global_checks import init_global_checks
from core.events import init_events from redbot.core.events import init_events
from core.sentry_setup import init_sentry_logging from redbot.core.sentry_setup import init_sentry_logging
from core.cli import interactive_config, confirm, parse_cli_flags, ask_sentry from redbot.core.cli import interactive_config, confirm, parse_cli_flags, ask_sentry
from core.core_commands import Core from redbot.core.core_commands import Core
from core.dev_commands import Dev from redbot.core.dev_commands import Dev
import asyncio import asyncio
import logging.handlers import logging.handlers
import logging import logging
import os import os
from pathlib import Path
from warnings import warn
# #
# Red - Discord Bot v3 # Red - Discord Bot v3
@ -58,7 +60,7 @@ def init_loggers(cli_flags):
else: else:
logger.setLevel(logging.WARNING) logger.setLevel(logging.WARNING)
from core.data_manager import core_data_path from redbot.core.data_manager import core_data_path
logfile_path = core_data_path() / 'red.log' logfile_path = core_data_path() / 'red.log'
fhandler = logging.handlers.RotatingFileHandler( fhandler = logging.handlers.RotatingFileHandler(
filename=str(logfile_path), encoding='utf-8', mode='a', filename=str(logfile_path), encoding='utf-8', mode='a',
@ -75,10 +77,6 @@ def init_loggers(cli_flags):
return logger, sentry_logger return logger, sentry_logger
def determine_main_folder() -> Path:
return Path(os.path.dirname(__file__)).resolve()
async def _get_prefix_and_token(red, indict): async def _get_prefix_and_token(red, indict):
""" """
Again, please blame <@269933075037814786> for this. Again, please blame <@269933075037814786> for this.
@ -90,48 +88,23 @@ async def _get_prefix_and_token(red, indict):
indict['enable_sentry'] = await red.db.enable_sentry() indict['enable_sentry'] = await red.db.enable_sentry()
if __name__ == '__main__': def main():
cli_flags = parse_cli_flags() cli_flags = parse_cli_flags(sys.argv[1:])
load_basic_configuration(cli_flags.instance_name)
if cli_flags.config:
load_basic_configuration(Path(cli_flags.config).resolve())
else:
warn("Soon you will need to change the way you load the bot."
" The new method of loading has yet to be decided upon but"
" will be made clear in announcements from the support server"
" and from documentation. Please see issue #938 for further"
" discussion on this topic.",
category=FutureWarning)
import core.data_manager
defaults = core.data_manager.basic_config_default.copy()
defaults['DATA_PATH'] = str(determine_main_folder())
defaults['CORE_PATH_APPEND'] = 'core/.data'
defaults['COG_PATH_APPEND'] = 'cogs/.data'
core.data_manager.basic_config = defaults
log, sentry_log = init_loggers(cli_flags) log, sentry_log = init_loggers(cli_flags)
description = "Red v3 - Alpha" description = "Red v3 - Alpha"
bot_dir = determine_main_folder() red = Red(cli_flags, description=description, pm_help=None)
red = Red(cli_flags, description=description, pm_help=None,
bot_dir=bot_dir)
init_global_checks(red) init_global_checks(red)
init_events(red, cli_flags) init_events(red, cli_flags)
red.add_cog(Core()) red.add_cog(Core())
red.add_cog(CogManagerUI()) red.add_cog(CogManagerUI())
if cli_flags.dev: if cli_flags.dev:
red.add_cog(Dev()) red.add_cog(Dev())
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
tmp_data = {} tmp_data = {}
loop.run_until_complete(_get_prefix_and_token(red, tmp_data)) loop.run_until_complete(_get_prefix_and_token(red, tmp_data))
token = os.environ.get("RED_TOKEN", tmp_data['token']) token = os.environ.get("RED_TOKEN", tmp_data['token'])
prefix = cli_flags.prefix or tmp_data['prefix'] prefix = cli_flags.prefix or tmp_data['prefix']
enable_sentry = tmp_data['enable_sentry']
if token is None or not prefix: if token is None or not prefix:
if cli_flags.no_prompt is False: if cli_flags.no_prompt is False:
new_token = interactive_config(red, token_set=bool(token), new_token = interactive_config(red, token_set=bool(token),
@ -141,17 +114,10 @@ if __name__ == '__main__':
else: else:
log.critical("Token and prefix must be set in order to login.") log.critical("Token and prefix must be set in order to login.")
sys.exit(1) sys.exit(1)
if enable_sentry is None:
ask_sentry(red)
loop.run_until_complete(_get_prefix_and_token(red, tmp_data)) loop.run_until_complete(_get_prefix_and_token(red, tmp_data))
if tmp_data['enable_sentry']: if tmp_data['enable_sentry']:
init_sentry_logging(red, sentry_log) init_sentry_logging(sentry_log)
cleanup_tasks = True cleanup_tasks = True
try: try:
loop.run_until_complete(red.start(token, bot=not cli_flags.not_bot)) loop.run_until_complete(red.start(token, bot=not cli_flags.not_bot))
except discord.LoginFailure: except discord.LoginFailure:
@ -183,3 +149,7 @@ if __name__ == '__main__':
gathered.exception() gathered.exception()
sys.exit(red._shutdown_mode.value) sys.exit(red._shutdown_mode.value)
if __name__ == '__main__':
main()

View File

@ -1,12 +1,13 @@
import discord
from copy import copy from copy import copy
from typing import Generator, Tuple, Iterable
import discord
from redbot.core import Config
from redbot.core.i18n import CogI18n
from redbot.core.utils.chat_formatting import box
from discord.ext import commands from discord.ext import commands
from typing import Generator, Tuple, Iterable from redbot.core.bot import Red
from core import Config
from core.bot import Red
from core.utils.chat_formatting import box
from core.i18n import CogI18n
from .alias_entry import AliasEntry from .alias_entry import AliasEntry
_ = CogI18n("Alias", __file__) _ = CogI18n("Alias", __file__)

View File

@ -1,8 +1,8 @@
from redbot.core import checks, bank
from redbot.core.i18n import CogI18n
from discord.ext import commands from discord.ext import commands
from core import checks, bank from redbot.core.bot import Red # Only used for type hints
from core.bot import Red # Only used for type hints
from core.i18n import CogI18n
_ = CogI18n('Bank', __file__) _ = CogI18n('Bank', __file__)

View File

@ -1,4 +1,4 @@
from core.bot import Red from redbot.core.bot import Red
from .downloader import Downloader from .downloader import Downloader

View File

@ -1,24 +1,23 @@
import os import os
import shutil import shutil
from pathlib import Path
from sys import path as syspath
from typing import Tuple, Union from typing import Tuple, Union
import discord import discord
from redbot.core import Config
from redbot.core import checks
from redbot.core.i18n import CogI18n
from redbot.core.utils.chat_formatting import box
from discord.ext import commands from discord.ext import commands
from pathlib import Path
from sys import path as syspath
from core import Config from redbot.core.bot import Red
from core.bot import Red
from core import checks
from core.utils.chat_formatting import box
from core.i18n import CogI18n
from .repo_manager import RepoManager, Repo
from .installable import Installable
from .converters import RepoName, InstalledCog
from .log import log
from .errors import CloningError, ExistingGitRepo
from .checks import install_agreement from .checks import install_agreement
from .converters import RepoName, InstalledCog
from .errors import CloningError, ExistingGitRepo
from .installable import Installable
from .log import log
from .repo_manager import RepoManager, Repo
_ = CogI18n('Downloader', __file__) _ = CogI18n('Downloader', __file__)

View File

@ -1,23 +1,22 @@
import asyncio import asyncio
import json import functools
import os import os
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from typing import Tuple, MutableMapping, Union
from subprocess import run as sp_run, PIPE
from sys import executable
import pkgutil import pkgutil
import shutil import shutil
import functools from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from subprocess import run as sp_run, PIPE
from sys import executable
from typing import Tuple, MutableMapping, Union
from discord.ext import commands from discord.ext import commands
from core import Config from redbot.core import Config
from core import data_manager from redbot.core import data_manager
from .errors import * from .errors import *
from .installable import Installable, InstallableType from .installable import Installable, InstallableType
from .log import log
from .json_mixins import RepoJSONMixin from .json_mixins import RepoJSONMixin
from .log import log
class Repo(RepoJSONMixin): class Repo(RepoJSONMixin):

View File

@ -1,5 +1,5 @@
from redbot.core.bot import Red
from .economy import Economy from .economy import Economy
from core.bot import Red
def setup(bot: Red): def setup(bot: Red):

View File

@ -5,12 +5,14 @@ from collections import defaultdict, deque
from enum import Enum from enum import Enum
import discord import discord
from redbot.cogs.bank import check_global_setting_guildowner, check_global_setting_admin
from redbot.core import Config, bank
from redbot.core.i18n import CogI18n
from redbot.core.utils.chat_formatting import pagify, box
from discord.ext import commands from discord.ext import commands
from core import checks, Config, bank
from core.utils.chat_formatting import pagify, box from redbot.core.bot import Red
from core.bot import Red
from cogs.bank import check_global_setting_guildowner, check_global_setting_admin
from core.i18n import CogI18n
_ = CogI18n("Economy", __file__) _ = CogI18n("Economy", __file__)

View File

@ -1,13 +1,15 @@
from discord.ext import commands
from core.utils.chat_formatting import escape, italics, pagify
from core.i18n import CogI18n
from random import randint, choice
from enum import Enum
from urllib.parse import quote_plus
import discord
import datetime import datetime
import time import time
from enum import Enum
from random import randint, choice
from urllib.parse import quote_plus
import aiohttp import aiohttp
import discord
from redbot.core.i18n import CogI18n
from discord.ext import commands
from redbot.core.utils.chat_formatting import escape, italics, pagify
_ = CogI18n("General", __file__) _ = CogI18n("General", __file__)

View File

@ -1,9 +1,10 @@
from discord.ext import commands
from random import shuffle from random import shuffle
import aiohttp
from core import checks, Config import aiohttp
from core.i18n import CogI18n from discord.ext import commands
from redbot.core.i18n import CogI18n
from redbot.core import checks, Config
_ = CogI18n("Image", __file__) _ = CogI18n("Image", __file__)

7
redbot/core/__init__.py Normal file
View File

@ -0,0 +1,7 @@
import pkg_resources
from .config import Config
__all__ = ["Config", "__version__"]
__version__ = version = pkg_resources.require("Red-DiscordBot")[0].version

View File

@ -1,10 +1,10 @@
import datetime import datetime
from typing import Union, List
import os import os
from typing import Union, List
import discord import discord
from core import Config from redbot.core import Config
__all__ = ["Account", "get_balance", "set_balance", "withdraw_credits", "deposit_credits", __all__ = ["Account", "get_balance", "set_balance", "withdraw_credits", "deposit_credits",
"can_spend", "transfer_credits", "wipe_bank", "get_guild_accounts", "can_spend", "transfer_credits", "wipe_bank", "get_guild_accounts",

View File

@ -1,19 +1,17 @@
import asyncio import asyncio
import os
from collections import Counter
from enum import Enum
from importlib.machinery import ModuleSpec from importlib.machinery import ModuleSpec
from pathlib import Path
import discord import discord
from discord.ext import commands from discord.ext import commands
from collections import Counter
from discord.ext.commands import GroupMixin from discord.ext.commands import GroupMixin
from pathlib import Path
from core import Config from .cog_manager import CogManager
from enum import Enum from . import Config
import os from . import i18n
from core.cog_manager import CogManager
from core import i18n
class Red(commands.Bot): class Red(commands.Bot):
@ -66,8 +64,7 @@ class Red(commands.Bot):
self.main_dir = bot_dir self.main_dir = bot_dir
self.cog_mgr = CogManager(paths=(str(self.main_dir / 'cogs'),), self.cog_mgr = CogManager(paths=(str(self.main_dir / 'cogs'),))
bot_dir=self.main_dir)
super().__init__(**kwargs) super().__init__(**kwargs)

View File

@ -1,7 +1,7 @@
import argparse import argparse
import asyncio import asyncio
from core.bot import Red from redbot.core.bot import Red
def confirm(m=""): def confirm(m=""):
@ -61,7 +61,7 @@ def ask_sentry(red: Red):
print("\nThank you for helping us with the development process!") print("\nThank you for helping us with the development process!")
def parse_cli_flags(): def parse_cli_flags(args):
parser = argparse.ArgumentParser(description="Red - Discord Bot") parser = argparse.ArgumentParser(description="Red - Discord Bot")
parser.add_argument("--owner", type=int, parser.add_argument("--owner", type=int,
help="ID of the owner. Only who hosts " help="ID of the owner. Only who hosts "
@ -102,11 +102,10 @@ def parse_cli_flags():
parser.add_argument("--dev", parser.add_argument("--dev",
action="store_true", action="store_true",
help="Enables developer mode") help="Enables developer mode")
parser.add_argument("config", parser.add_argument("instance_name",
nargs='?', help="Name of the bot instance created during `redbot-setup`.")
help="Path to config generated on initial setup.")
args = parser.parse_args() args = parser.parse_args(args)
if args.prefix: if args.prefix:
args.prefix = sorted(args.prefix, reverse=True) args.prefix = sorted(args.prefix, reverse=True)

View File

@ -1,15 +1,16 @@
import pkgutil import pkgutil
from importlib import invalidate_caches from importlib import invalidate_caches
from importlib.machinery import ModuleSpec from importlib.machinery import ModuleSpec
from typing import Tuple, Union, List
from pathlib import Path from pathlib import Path
from typing import Tuple, Union, List
from . import checks
from .config import Config
from .i18n import CogI18n
from .data_manager import cog_data_path
from discord.ext import commands from discord.ext import commands
from core import checks from .utils.chat_formatting import box
from core.config import Config
from core.utils.chat_formatting import box
from core.i18n import CogI18n
__all__ = ["CogManager"] __all__ = ["CogManager"]
@ -20,11 +21,13 @@ class CogManager:
directory. You may also set a directory for downloader to install new cogs to, the default directory. You may also set a directory for downloader to install new cogs to, the default
being the :code:`cogs/` folder in the root bot directory. being the :code:`cogs/` folder in the root bot directory.
""" """
def __init__(self, paths: Tuple[str]=(), bot_dir: Path=Path.cwd()): def __init__(self, paths: Tuple[str]=()):
self.conf = Config.get_conf(self, 2938473984732, True) self.conf = Config.get_conf(self, 2938473984732, True)
tmp_cog_install_path = cog_data_path(self) / "cogs"
tmp_cog_install_path.mkdir(parents=True, exist_ok=True)
self.conf.register_global( self.conf.register_global(
paths=(), paths=(),
install_path=str(bot_dir.resolve() / "cogs") install_path=str(tmp_cog_install_path)
) )
self._paths = list(paths) self._paths = list(paths)

View File

@ -1,14 +1,11 @@
import logging import logging
from copy import deepcopy
from typing import Callable, Union, Tuple from typing import Callable, Union, Tuple
import discord import discord
from copy import deepcopy
from pathlib import Path
from .data_manager import cog_data_path, core_data_path
from .drivers.red_json import JSON as JSONDriver from .drivers.red_json import JSON as JSONDriver
from core.data_manager import cog_data_path, core_data_path
log = logging.getLogger("red.config") log = logging.getLogger("red.config")

View File

@ -1,16 +1,20 @@
import itertools
from discord.ext import commands
from core import checks
from core import i18n
from string import ascii_letters, digits
from random import SystemRandom
from collections import namedtuple
import logging
import importlib
import sys
import discord
import aiohttp
import asyncio import asyncio
import importlib
import itertools
import logging
import sys
from collections import namedtuple
from random import SystemRandom
from string import ascii_letters, digits
import aiohttp
import discord
from discord.ext import commands
from redbot.core import checks
from redbot.core import i18n
import redbot.cogs # Don't remove this line or core cogs won't load
log = logging.getLogger("red") log = logging.getLogger("red")
@ -32,9 +36,14 @@ class Core:
try: try:
spec = await ctx.bot.cog_mgr.find_cog(cog_name) spec = await ctx.bot.cog_mgr.find_cog(cog_name)
except RuntimeError: except RuntimeError:
real_name = ".{}".format(cog_name)
try:
mod = importlib.import_module(real_name, package='redbot.cogs')
except ImportError as e:
await ctx.send(_("No module by that name was found in any" await ctx.send(_("No module by that name was found in any"
" cog path.")) " cog path."))
return return
spec = mod.__spec__
try: try:
ctx.bot.load_extension(spec) ctx.bot.load_extension(spec)
@ -335,7 +344,8 @@ class Core:
# Since it can also be set through cli flags, bot.db is not a reliable # Since it can also be set through cli flags, bot.db is not a reliable
# source. So we'll just mock a DM message instead. # source. So we'll just mock a DM message instead.
fake_message = namedtuple('Message', 'guild') fake_message = namedtuple('Message', 'guild')
prefix = ctx.bot.command_prefix(ctx.bot, fake_message(guild=None))[0] prefixes = await ctx.bot.command_prefix(ctx.bot, fake_message(guild=None))
prefix = prefixes[0]
content = _("Use `{}dm {} <text>` to reply to this user" content = _("Use `{}dm {} <text>` to reply to this user"
"").format(prefix, author.id) "").format(prefix, author.id)
@ -384,7 +394,8 @@ class Core:
e = discord.Embed(colour=discord.Colour.red(), description=message) e = discord.Embed(colour=discord.Colour.red(), description=message)
description = _("Owner of %s") % ctx.bot.user description = _("Owner of %s") % ctx.bot.user
fake_message = namedtuple('Message', 'guild') fake_message = namedtuple('Message', 'guild')
prefix = ctx.bot.command_prefix(ctx.bot, fake_message(guild=None))[0] prefixes = await ctx.bot.command_prefix(ctx.bot, fake_message(guild=None))
prefix = prefixes[0]
e.set_footer(text=_("You can reply to this message with %scontact" e.set_footer(text=_("You can reply to this message with %scontact"
"") % prefix) "") % prefix)
if ctx.bot.user.avatar_url: if ctx.bot.user.avatar_url:

View File

@ -1,6 +1,9 @@
import sys
from pathlib import Path from pathlib import Path
from core.json_io import JsonIO import appdirs
from .json_io import JsonIO
jsonio = None jsonio = None
basic_config = None basic_config = None
@ -11,13 +14,23 @@ basic_config_default = {
"CORE_PATH_APPEND": "core" "CORE_PATH_APPEND": "core"
} }
config_dir = Path(appdirs.AppDirs("Red-DiscordBot").user_config_dir)
config_file = config_dir / 'config.json'
def load_basic_configuration(path: Path):
def load_basic_configuration(instance_name: str):
global jsonio global jsonio
global basic_config global basic_config
jsonio = JsonIO(path) jsonio = JsonIO(config_file)
basic_config = jsonio._load_json()
try:
config = jsonio._load_json()
basic_config = config[instance_name]
except (FileNotFoundError, KeyError):
print("You need to configure the bot instance using `redbot-setup`"
" prior to running the bot.")
sys.exit(1)
def _base_data_path() -> Path: def _base_data_path() -> Path:

View File

@ -1,15 +1,16 @@
from discord.ext import commands
from core.utils.chat_formatting import box, pagify
from core import checks
from core.i18n import CogI18n
import asyncio import asyncio
import discord
import traceback
import inspect import inspect
import textwrap
from contextlib import redirect_stdout
import io import io
import textwrap
import traceback
from contextlib import redirect_stdout
import discord
from . import checks
from .i18n import CogI18n
from discord.ext import commands
from .utils.chat_formatting import box, pagify
""" """
Notice: Notice:

View File

@ -1,9 +1,9 @@
from pathlib import Path
from typing import Tuple from typing import Tuple
from core.drivers.red_base import BaseDriver from ..json_io import JsonIO
from core.json_io import JsonIO
from pathlib import Path from .red_base import BaseDriver
class JSON(BaseDriver): class JSON(BaseDriver):

View File

@ -1,10 +1,12 @@
import discord
import traceback
import datetime import datetime
import logging import logging
import traceback
import discord
from .sentry_setup import should_log
from discord.ext import commands from discord.ext import commands
from core.utils.chat_formatting import inline
from core.sentry_setup import should_log from .utils.chat_formatting import inline
log = logging.getLogger("red") log = logging.getLogger("red")
sentry_log = logging.getLogger("red.sentry") sentry_log = logging.getLogger("red.sentry")

View File

View File

@ -1,9 +1,7 @@
from raven import Client, breadcrumbs from raven import Client, breadcrumbs
from raven.versioning import fetch_git_sha
from raven.conf import setup_logging
from raven.handlers.logging import SentryHandler from raven.handlers.logging import SentryHandler
from pathlib import Path from redbot.core import __version__
__all__ = ("init_sentry_logging", "should_log") __all__ = ("init_sentry_logging", "should_log")
@ -27,12 +25,12 @@ include_paths = (
client = None client = None
def init_sentry_logging(bot, logger): def init_sentry_logging(logger):
global client global client
client = Client( client = Client(
dsn=("https://27f3915ba0144725a53ea5a99c9ae6f3:87913fb5d0894251821dcf06e5e9cfe6@" dsn=("https://27f3915ba0144725a53ea5a99c9ae6f3:87913fb5d0894251821dcf06e5e9cfe6@"
"sentry.telemetry.red/19?verify_ssl=0"), "sentry.telemetry.red/19?verify_ssl=0"),
release=fetch_git_sha(str(bot.main_dir)) release=__version__
) )
breadcrumbs.ignore_logger("websockets") breadcrumbs.ignore_logger("websockets")

View File

107
redbot/setup.py Normal file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python
import sys
from copy import deepcopy
from pathlib import Path
import appdirs
from redbot.core.json_io import JsonIO
from redbot.core.data_manager import basic_config_default
from redbot.core.cli import confirm
appdir = appdirs.AppDirs("Red-DiscordBot")
config_dir = Path(appdir.user_config_dir)
config_dir.mkdir(parents=True, exist_ok=True)
config_file = config_dir / 'config.json'
def load_existing_config():
if not config_file.exists():
return {}
return JsonIO(config_file)._load_json()
def save_config(name, data):
config = load_existing_config()
config[name] = data
JsonIO(config_file)._save_json(config)
def basic_setup():
"""
Creates the data storage folder.
:return:
"""
default_data_dir = Path(appdir.user_data_dir)
print("Hello! Before we begin the full configuration process we need to"
" gather some initial information about where you'd like us"
" to store your bot's data. We've attempted to figure out a"
" sane default data location which is printed below. If you don't"
" want to change this default please press [ENTER], otherwise"
" input your desired data location.")
print()
print("Default: {}".format(default_data_dir))
new_path = input('> ')
if new_path != '':
new_path = Path(new_path)
default_data_dir = new_path
if not default_data_dir.exists():
try:
default_data_dir.mkdir(parents=True, exist_ok=True)
except OSError:
print("We were unable to create your chosen directory."
" You may need to restart this process with admin"
" privileges.")
sys.exit(1)
print("You have chosen {} to be your data directory."
"".format(default_data_dir))
if not confirm("Please confirm (y/n):"):
print("Please start the process over.")
sys.exit(0)
default_dirs = deepcopy(basic_config_default)
default_dirs['DATA_PATH'] = str(default_data_dir.resolve())
storage_dict = {
1: "JSON",
2: "MongoDB"
}
storage = None
while storage is None:
print()
print("Please choose your storage backend (if you're unsure, choose 1).")
print("1. JSON (file storage, requires no database).")
print("2. MongoDB")
storage = input("> ")
try:
storage = int(storage)
except ValueError:
storage = None
else:
if storage not in storage_dict:
storage = None
default_dirs['STORAGE_TYPE'] = storage_dict[storage]
name = ""
while len(name) == 0:
print()
print("Please enter a name for your instance, this name cannot include spaces"
" and it will be used to run your bot from here on out.")
name = input("> ")
if " " in name:
name = ""
save_config(name, default_dirs)
print()
print("Your basic configuration has been saved. Please run `redbot <name>` to"
" continue your setup process and to run the bot.")

View File

@ -1,6 +1,4 @@
git+https://github.com/Rapptz/discord.py@rewrite#egg=discord.py[voice] appdirs
discord.py>=1.0.0a0
youtube_dl youtube_dl
pytest raven
git+https://github.com/pytest-dev/pytest-asyncio
pymongo
git+https://github.com/getsentry/raven-python

2
setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[metadata]
long_description = file: README.rst

111
setup.py Normal file
View File

@ -0,0 +1,111 @@
from distutils.core import setup
from pathlib import Path
from subprocess import run, PIPE
from setuptools import find_packages
def get_package_list():
core = find_packages(include=['redbot', 'redbot.*'])
return core
def get_requirements():
with open('requirements.txt') as f:
requirements = f.read().splitlines()
return requirements
def get_version():
try:
p = run(
"git describe --abbrev=0 --tags".split(),
stdout=PIPE
)
except FileNotFoundError:
# No git
return 3, 0, 0
if p.returncode != 0:
return 3, 0, 0
stdout = p.stdout.strip().decode()
if stdout.startswith("v"):
numbers = stdout[1:].split('.')
args = [0, 0, 0]
for i in range(3):
try:
args[i] = int(numbers[i])
except (IndexError, ValueError):
args[i] = 0
return args
return 3, 0, 0
def find_locale_folders():
"""
Ignore this tomfoolery in the desire for automation. It works, that's
all you gotta know. Don't fuck with this unless you really know what
you're doing, otherwise we lose all translations.
"""
def glob_locale_files(path: Path):
msgs = path.glob("*.po")
parents = path.parents
return [str(m.relative_to(parents[0])) for m in msgs]
ret = {
'redbot.core': glob_locale_files(Path('redbot/core/locales'))
}
cogs_path = Path('redbot/cogs')
for cog_folder in cogs_path.iterdir():
locales_folder = cog_folder / 'locales'
if not locales_folder.is_dir():
continue
pkg_name = str(cog_folder).replace('/', '.')
ret[pkg_name] = glob_locale_files(locales_folder)
return ret
setup(
name='Red-DiscordBot',
version="{}.{}.{}a13".format(*get_version()),
packages=get_package_list(),
package_data=find_locale_folders(),
url='https://github.com/Cog-Creators/Red-DiscordBot',
license='GPLv3',
author='Cog-Creators',
author_email='',
description='A highly customizable Discord bot',
classifiers=[
'Development Status :: 4 - Beta',
'Framework :: AsyncIO',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Communications :: Chat',
'Topic :: Documentation :: Sphinx'
],
entry_points={
'console_scripts': [
'redbot=redbot.__main__:main',
'redbot-setup=redbot.setup:basic_setup']
},
python_requires='>=3.5',
setup_requires=get_requirements(),
install_requires=get_requirements(),
dependency_links=[
'https://github.com/Rapptz/discord.py/tarball/rewrite#egg=discord.py-1.0'
],
extras_require={
'test': ['pytest>=3', 'pytest-asyncio'],
'mongo': ['pymongo', 'motor'],
'docs': ['sphinx', 'sphinxcontrib-asyncio', 'sphinx_rtd_theme']
}
)

View File

@ -1,9 +1,10 @@
from collections import namedtuple from collections import namedtuple
from raven.versioning import fetch_git_sha from pathlib import Path
import pytest import pytest
from cogs.downloader.repo_manager import RepoManager, Repo from raven.versioning import fetch_git_sha
from pathlib import Path
from redbot.cogs.downloader.repo_manager import RepoManager, Repo
async def fake_run(*args, **kwargs): async def fake_run(*args, **kwargs):
@ -37,8 +38,6 @@ def repo_manager(tmpdir_factory, config):
@pytest.fixture @pytest.fixture
def repo(tmpdir): def repo(tmpdir):
from cogs.downloader.repo_manager import Repo
repo_folder = Path(str(tmpdir)) / 'repos' / 'squid' repo_folder = Path(str(tmpdir)) / 'repos' / 'squid'
repo_folder.mkdir(parents=True, exist_ok=True) repo_folder.mkdir(parents=True, exist_ok=True)
@ -69,8 +68,6 @@ def bot_repo(event_loop):
def test_existing_git_repo(tmpdir): def test_existing_git_repo(tmpdir):
from cogs.downloader.repo_manager import Repo
repo_folder = Path(str(tmpdir)) / 'repos' / 'squid' / '.git' repo_folder = Path(str(tmpdir)) / 'repos' / 'squid' / '.git'
repo_folder.mkdir(parents=True, exist_ok=True) repo_folder.mkdir(parents=True, exist_ok=True)
@ -103,7 +100,7 @@ async def test_clone_repo(repo_norun, capsys):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_add_repo(monkeypatch, repo_manager): async def test_add_repo(monkeypatch, repo_manager):
monkeypatch.setattr("cogs.downloader.repo_manager.Repo._run", monkeypatch.setattr("redbot.cogs.downloader.repo_manager.Repo._run",
fake_run_noprint) fake_run_noprint)
squid = await repo_manager.add_repo( squid = await repo_manager.add_repo(

View File

@ -1,9 +1,10 @@
import pytest
import json import json
from cogs.downloader.installable import Installable, InstallableType
from pathlib import Path from pathlib import Path
import pytest
from redbot.cogs.downloader.installable import Installable, InstallableType
INFO_JSON = { INFO_JSON = {
"author": ( "author": (
"tekulvw", "tekulvw",

View File

@ -1,12 +1,13 @@
from cogs.alias import Alias
import pytest import pytest
from redbot.cogs.alias import Alias
@pytest.fixture() @pytest.fixture()
def alias(config): def alias(config):
import cogs.alias.alias import redbot.cogs.alias.alias
cogs.alias.alias.Config.get_conf = lambda *args, **kwargs: config redbot.cogs.alias.alias.Config.get_conf = lambda *args, **kwargs: config
return Alias(None) return Alias(None)

View File

@ -3,10 +3,10 @@ import pytest
@pytest.fixture() @pytest.fixture()
def bank(config): def bank(config):
from core import Config from redbot.core import Config
Config.get_conf = lambda *args, **kwargs: config Config.get_conf = lambda *args, **kwargs: config
from core import bank from redbot.core import bank
bank._register_defaults() bank._register_defaults()
return bank return bank

View File

@ -1,13 +1,13 @@
import random
from collections import namedtuple from collections import namedtuple
from pathlib import Path from pathlib import Path
import pytest import pytest
import random
from core.bot import Red
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
from core.drivers import red_json from redbot.core import Config
from core import Config from redbot.core.bot import Red
from redbot.core.drivers import red_json
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
@ -19,7 +19,7 @@ def monkeysession(request):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def override_data_path(tmpdir): def override_data_path(tmpdir):
from core import data_manager from redbot.core import data_manager
data_manager.basic_config = data_manager.basic_config_default data_manager.basic_config = data_manager.basic_config_default
data_manager.basic_config['DATA_PATH'] = str(tmpdir) data_manager.basic_config['DATA_PATH'] = str(tmpdir)
@ -145,8 +145,8 @@ def ctx(empty_member, empty_channel, red):
#region Red Mock #region Red Mock
@pytest.fixture() @pytest.fixture()
def red(config_fr): def red(config_fr):
from core.cli import parse_cli_flags from redbot.core.cli import parse_cli_flags
cli_flags = parse_cli_flags() cli_flags = parse_cli_flags(["ignore_me"])
description = "Red v3 - Alpha" description = "Red v3 - Alpha"

View File

@ -1,7 +1,8 @@
from pathlib import Path from pathlib import Path
import pytest import pytest
from core import cog_manager
from redbot.core import cog_manager
@pytest.fixture() @pytest.fixture()
@ -14,9 +15,10 @@ def default_dir(red):
return red.main_dir return red.main_dir
@pytest.mark.skip
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_ensure_cogs_in_paths(cog_mgr, default_dir): async def test_ensure_cogs_in_paths(cog_mgr, default_dir):
cogs_dir = default_dir / 'cogs' cogs_dir = default_dir / 'redbot' / 'cogs'
assert cogs_dir in await cog_mgr.paths() assert cogs_dir in await cog_mgr.paths()

View File

@ -1,9 +1,10 @@
import json import json
from pathlib import Path from pathlib import Path
from core import data_manager
import pytest import pytest
from redbot.core import data_manager
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def cleanup_datamanager(): def cleanup_datamanager():
@ -32,6 +33,7 @@ def test_no_basic(cog_instance):
data_manager.cog_data_path(cog_instance) data_manager.cog_data_path(cog_instance)
@pytest.mark.skip
def test_core_path(data_mgr_config, tmpdir): def test_core_path(data_mgr_config, tmpdir):
conf_path = tmpdir.join('config.json') conf_path = tmpdir.join('config.json')
conf_path.write(json.dumps(data_mgr_config)) conf_path.write(json.dumps(data_mgr_config))

View File

@ -1,10 +1,11 @@
from core import sentry_setup
import logging import logging
from redbot.core import sentry_setup
def test_sentry_capture(red): def test_sentry_capture(red):
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
sentry_setup.init_sentry_logging(red, log) sentry_setup.init_sentry_logging(log)
assert sentry_setup.client is not None assert sentry_setup.client is not None

View File

@ -1,6 +1,6 @@
import core from redbot import core
def test_version_working(): def test_version_working():
assert hasattr(core, '__version__') assert hasattr(core, '__version__')
assert core.__version__ >= (3, 0, 0) assert core.__version__[0] == "3"