mirror of
https://github.com/UiO-Gaming/ShiteMusicBot.git
synced 2026-11-05 13:56:51 +01:00
33 files changed
+561
-163
No files matched your search
@@ -0,0 +1,73 @@
|
||||
name: Test And Build BOT
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
flake8:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CheckFiles: "bot.py cogs/"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
pip install flake8
|
||||
flake8 ${CheckFiles}
|
||||
|
||||
- name: Check with Isort
|
||||
run: |
|
||||
pip install isort
|
||||
isort --check --sp setup.cfg ${CheckFiles}
|
||||
- name: Sort with Isort
|
||||
if: failure()
|
||||
run: |
|
||||
isort --sp setup.cfg ${CheckFiles}
|
||||
- name: Create Pull Request
|
||||
if: failure()
|
||||
uses: peter-evans/create-pull-request@v2
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
branch: actions/auto
|
||||
commit-message: Changes done by tests
|
||||
committer: Roxedus <bot@roxedus.dev>
|
||||
title: Linter changed files
|
||||
|
||||
build-and-push:
|
||||
needs: flake8
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
repo: rnorge/music
|
||||
ref: ${{github.ref}}
|
||||
DOCKER_USERNAME: roxedus
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
branch="${ref##*/}"
|
||||
docker build -t $repo:$branch -t $repo:$branch-${{github.sha}} .
|
||||
|
||||
- name: Master tag
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
branch="${ref##*/}"
|
||||
bot_ver=$(grep -i bot_version cogs/utils/bot_version.py | cut -d" " -f3 | tr -d \")
|
||||
docker tag $repo:$branch $repo:$bot_ver
|
||||
|
||||
- name: Bump version and push tag
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: mathieudutour/github-tag-action@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to registry
|
||||
if: github.event.pull_request == false
|
||||
run: echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
|
||||
|
||||
- name: Pushes the Docker image
|
||||
if: github.event.pull_request == false
|
||||
run: docker push $repo
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"oderwat.indent-rainbow",
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance"
|
||||
]
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"name": "Debug Bot",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "bot.py",
|
||||
"console": "integratedTerminal",
|
||||
"args": ["-D", "-d", "data"],
|
||||
"host": "localhost"
|
||||
}]
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"python.languageSever": "Pylance",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.pycodestyleEnabled": true,
|
||||
"[python]": {
|
||||
"editor.rulers": [120],
|
||||
},
|
||||
"[Dockerfile]": {
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"editor.tabSize": 4
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
FROM amd64/python:3.7.3-alpine
|
||||
FROM amd64/python:3.8.3-alpine
|
||||
|
||||
LABEL maintainer="Roxedus" jekins="dorli"
|
||||
LABEL maintainer="Roxedus"
|
||||
|
||||
COPY / /app
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# WIP - Shite Music Bot
|
||||
|
||||
[](https://jenkins.roxedus.net/job/MusicBot/job/dev/)
|
||||
[](https://microbadger.com/images/rnorge/music:dev-amd64 "Get your own image badge on microbadger.com")
|
||||
[](https://microbadger.com/images/rnorge/music:dev-amd64 "Get your own image badge on microbadger.com")
|
||||
|
||||
## Features
|
||||
|
||||
### Features:
|
||||
- Translations, get the bot in your preferred langauge!
|
||||
- Round Robin style queue, you won't have to wait forever for your songs to play.
|
||||
- Noice per server customization options.
|
||||
@@ -12,18 +13,19 @@
|
||||
- Lyrics command that sometimes gives the correct lyrics
|
||||
- DJ roles
|
||||
|
||||
### Setup
|
||||
|
||||
#### Setup
|
||||
1. Change or copy data/config.yaml.example to config.yaml
|
||||
2. Create or find a [lavalink](https://github.com/Frederikam/Lavalink) server you can use.
|
||||
3. Add your bot token and lavalink server.
|
||||
4. Edit any other settings you want.
|
||||
5. install requirements
|
||||
> python -m pip install -r requirements.txt
|
||||
5. install requirements `python -m pip install -r requirements.txt`
|
||||
6. run the bot :)
|
||||
|
||||
#### Docker
|
||||
#### Compose Example:
|
||||
### Docker
|
||||
|
||||
### Compose Example
|
||||
|
||||
````yaml
|
||||
version: '3'
|
||||
networks:
|
||||
@@ -42,7 +44,7 @@ services:
|
||||
- lavalink
|
||||
volumes:
|
||||
- /compose/BottStack/lavalink/application.yml:/opt/Lavalink/application.yml
|
||||
bot:
|
||||
bot:
|
||||
container_name: ProdMaster
|
||||
image: rnorge/music
|
||||
networks:
|
||||
@@ -51,4 +53,4 @@ services:
|
||||
volumes:
|
||||
- /compose/BottStack/ProdBetaMaster/script:/diks
|
||||
- /compose/BottStack/ProdBetaMaster/data:/app/data
|
||||
````
|
||||
````
|
||||
@@ -1,23 +1,25 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
import codecs
|
||||
import time
|
||||
import os
|
||||
import traceback
|
||||
import yaml
|
||||
import aiohttp
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from argparse import ArgumentParser, RawTextHelpFormatter
|
||||
from cogs.utils.settingsmanager import Settings
|
||||
from cogs.utils.localizer import Localizer
|
||||
from cogs.utils.localizer import LocalizerWrapper
|
||||
|
||||
import aiohttp
|
||||
import yaml
|
||||
|
||||
# Bot Utilities
|
||||
from cogs.utils.alias import Aliaser
|
||||
from cogs.utils.context import Context
|
||||
from cogs.utils.localizer import Localizer, LocalizerWrapper
|
||||
from cogs.utils.logger import BotLogger
|
||||
from cogs.helpformatter import commandhelper
|
||||
from cogs.utils.paginator import Scroller
|
||||
from cogs.utils.settingsmanager import Settings
|
||||
|
||||
initial_extensions = [
|
||||
'cogs.errors',
|
||||
'cogs.cogs',
|
||||
'cogs.settings',
|
||||
'cogs.misc',
|
||||
@@ -25,8 +27,7 @@ initial_extensions = [
|
||||
]
|
||||
|
||||
on_ready_extensions = [
|
||||
'cogs.music',
|
||||
'cogs.musicevents'
|
||||
'cogs.nodemanager'
|
||||
]
|
||||
|
||||
|
||||
@@ -49,8 +50,6 @@ class Bot(commands.Bot):
|
||||
self.localizer = Localizer(conf.get('locale path', "./localization"), conf.get('locale', 'en_en'))
|
||||
self.aliaser = Aliaser(conf.get('locale path', "./localization"), conf.get('locale', 'en_en'))
|
||||
|
||||
self.session = aiohttp.ClientSession(loop=self.loop)
|
||||
|
||||
self.datadir = datadir
|
||||
self.debug = debug
|
||||
self.main_logger = logger
|
||||
@@ -63,42 +62,6 @@ class Bot(commands.Bot):
|
||||
except Exception:
|
||||
self.logger.exception("Loading of extension %s failed" % extension)
|
||||
|
||||
async def on_command_error(self, ctx, err):
|
||||
if not self.debug:
|
||||
if (isinstance(err, commands.MissingRequiredArgument) or
|
||||
isinstance(err, commands.BadArgument)):
|
||||
paginator = commandhelper(ctx, ctx.command, ctx.invoker, include_subcmd=False)
|
||||
scroller = Scroller(ctx, paginator)
|
||||
await scroller.start_scrolling()
|
||||
|
||||
if isinstance(err, commands.CommandInvokeError):
|
||||
self.logger.debug("Error running command: %s\n Traceback: %s" % (ctx.command, err))
|
||||
pass
|
||||
|
||||
elif isinstance(err, commands.NoPrivateMessage):
|
||||
self.logger.debug("Error running command: %s\n Traceback: %s" % (ctx.command, err))
|
||||
await ctx.send('That command is not available in DMs')
|
||||
|
||||
elif isinstance(err, commands.CommandOnCooldown):
|
||||
await ctx.send(f"{ctx.message.author.mention} Command is on cooldown. "
|
||||
f"Try again in `{err.retry_after:.1f}` seconds.")
|
||||
|
||||
elif isinstance(err, RuntimeError):
|
||||
self.logger.debug("Error running command: %s\n Traceback: %s" % (ctx.command, err))
|
||||
pass
|
||||
|
||||
elif isinstance(err, commands.CheckFailure):
|
||||
self.logger.debug("Error running command: %s\n Traceback: %s" % (ctx.command, err))
|
||||
pass
|
||||
|
||||
elif isinstance(err, commands.CommandNotFound):
|
||||
pass
|
||||
else:
|
||||
tb = err.__traceback__
|
||||
traceback.print_tb(tb)
|
||||
print(err)
|
||||
self.logger.debug("Error running command: %s\n Traceback: %s" % (ctx.command, err))
|
||||
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return
|
||||
@@ -121,6 +84,7 @@ class Bot(commands.Bot):
|
||||
async def on_ready(self):
|
||||
if not hasattr(self, 'uptime'):
|
||||
self.uptime = time.time()
|
||||
|
||||
for extension in on_ready_extensions:
|
||||
try:
|
||||
self.logger.debug("Loading extension %s" % extension)
|
||||
@@ -133,8 +97,9 @@ class Bot(commands.Bot):
|
||||
print(f'Version: {discord.__version__}\n')
|
||||
self.logger.debug("Bot Ready\n\n\n")
|
||||
|
||||
self.session = aiohttp.ClientSession(loop=self.loop)
|
||||
await self.change_presence(activity=discord.Game(type=0,
|
||||
name=conf["bot"]["playing status"]),
|
||||
name=conf["bot"]["playing status"]),
|
||||
status=discord.Status.online)
|
||||
|
||||
def run(self):
|
||||
|
||||
+8
-4
@@ -1,6 +1,8 @@
|
||||
import traceback
|
||||
# Discord Packages
|
||||
from discord.ext import commands
|
||||
|
||||
import traceback
|
||||
|
||||
|
||||
class Cogs(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
@@ -21,13 +23,15 @@ class Cogs(commands.Cog):
|
||||
try:
|
||||
self.bot.load_extension(f'cogs.{module}')
|
||||
await ctx.send(f'{module} loaded')
|
||||
except Exception as e:
|
||||
await ctx.send(f'```py\n{traceback.format_exc(e)}\n```')
|
||||
except Exception:
|
||||
await ctx.send(f'```py\n{traceback.format_exc()}\n```')
|
||||
|
||||
@_cogs.command()
|
||||
@commands.is_owner()
|
||||
async def unload(self, ctx, *, module):
|
||||
"""Unloads a module."""
|
||||
if module == "cogs":
|
||||
return await ctx.send('Unloading this cog is not allowed')
|
||||
try:
|
||||
self.bot.unload_extension(f'cogs.{module}')
|
||||
await ctx.send(f'{module} unloaded')
|
||||
@@ -63,7 +67,7 @@ class Cogs(commands.Cog):
|
||||
@commands.is_owner()
|
||||
async def _shutdown(self, ctx):
|
||||
"""Logs out and stops."""
|
||||
self.bot.lavalink.players.clear()
|
||||
self.bot.lavalink.player_manager.players.clear()
|
||||
await self.bot.logout()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
import traceback
|
||||
|
||||
# Bot Utilities
|
||||
from cogs.helpformatter import commandhelper
|
||||
from cogs.utils.paginator import Scroller
|
||||
|
||||
|
||||
class Errors(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.logger = self.bot.main_logger.bot_logger.getChild("Errors")
|
||||
|
||||
async def base_msg(self, ctx, state: int = 0xFCBA03):
|
||||
embed = discord.Embed(color=state)
|
||||
embed.title = '{errors.error_occurred}'
|
||||
embed.set_footer(icon_url=ctx.author.avatar_url,
|
||||
text=f'{ctx.author.name}#{ctx.author.discriminator}')
|
||||
return embed
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_command_error(self, ctx, err):
|
||||
|
||||
# Help commands
|
||||
if (isinstance(err, commands.MissingRequiredArgument) or
|
||||
isinstance(err, commands.BadArgument)):
|
||||
paginator = commandhelper(ctx, ctx.command, ctx.invoker, include_subcmd=False)
|
||||
scroller = Scroller(ctx, paginator)
|
||||
await scroller.start_scrolling()
|
||||
|
||||
if isinstance(err, (commands.CommandNotFound)):
|
||||
return
|
||||
|
||||
async def send_error_embed(description):
|
||||
embed = await self.base_msg(ctx, state=0xFC0303)
|
||||
embed.description = description
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
return await ctx.send(embed=embed)
|
||||
|
||||
# Music related errors
|
||||
if isinstance(err, commands.CommandInvokeError):
|
||||
if (err.original == 'Join a voicechannel first.'):
|
||||
return await send_error_embed('{errors.join_voice_first}')
|
||||
|
||||
elif (err.original == 'Not connected.'):
|
||||
return await send_error_embed('{errors.not_connected}')
|
||||
|
||||
elif (err.original == 'I need the `CONNECT` and `SPEAK` permissions.'):
|
||||
return await send_error_embed('{errors.need_permission}')
|
||||
|
||||
elif (err.original == 'You need to be in the right voice channel'):
|
||||
return await send_error_embed('{errors.right_channel}')
|
||||
|
||||
elif (err.original == 'You need to be in my voicechannel.'):
|
||||
return await send_error_embed('{errors.my_channel}')
|
||||
|
||||
if isinstance(err, commands.CommandOnCooldown):
|
||||
await ctx.send(f"{ctx.message.author.mention} Command is on cooldown. "
|
||||
f"Try again in `{err.retry_after:.1f}` seconds.")
|
||||
|
||||
elif isinstance(err, commands.NoPrivateMessage):
|
||||
await ctx.send('That command is not available in DMs')
|
||||
|
||||
else:
|
||||
# Log all exceptions if the bot is in debug mode
|
||||
if self.bot.debug:
|
||||
tb = err.__traceback__
|
||||
traceback.print_tb(tb)
|
||||
self.logger.debug("Error running command: %s\nTraceback: %s" % (ctx.command, err))
|
||||
|
||||
else:
|
||||
to_log = (RuntimeError, commands.CheckFailure, commands.CommandInvokeError,
|
||||
commands.NoPrivateMessage)
|
||||
|
||||
if isinstance(err, to_log):
|
||||
self.logger.debug("Error running command: %s\nTraceback: %s" % (ctx.command, err))
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Errors(bot))
|
||||
@@ -1,9 +1,11 @@
|
||||
# Discord Packages
|
||||
from discord.ext import commands
|
||||
|
||||
import re
|
||||
|
||||
# Bot Utilities
|
||||
from cogs.utils.paginator import HelpPaginator, Scroller
|
||||
|
||||
|
||||
usermention = r"<@!?\d{17,19}>"
|
||||
|
||||
|
||||
|
||||
+8
-5
@@ -1,11 +1,14 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
import time
|
||||
import platform
|
||||
|
||||
from discord.ext import commands
|
||||
from cogs.utils import bot_version
|
||||
from lavalink import __version__ as LavalinkVersion
|
||||
|
||||
import platform
|
||||
import time
|
||||
|
||||
# Bot Utilities
|
||||
from cogs.utils import bot_version
|
||||
|
||||
|
||||
class Misc(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
@@ -51,7 +54,7 @@ class Misc(commands.Cog):
|
||||
lavalink = self.bot.lavalink
|
||||
|
||||
listeners = 0
|
||||
for guild, player in lavalink.player_manager.players:
|
||||
for guild, player in lavalink.player_manager.players.items():
|
||||
listeners += len(player.listeners)
|
||||
|
||||
embed.add_field(name='{music.players}', value=f'{len(lavalink.player_manager.players)}')
|
||||
|
||||
+33
-33
@@ -1,25 +1,22 @@
|
||||
"""
|
||||
Music commands
|
||||
"""
|
||||
# Discord Packages
|
||||
import discord
|
||||
import lavalink
|
||||
from discord.ext import commands
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import asyncio
|
||||
import yaml
|
||||
import codecs
|
||||
|
||||
import lavalink
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import urllib
|
||||
from typing import Optional
|
||||
|
||||
import urllib
|
||||
import json
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from .utils import checks, RoxUtils, timeformatter
|
||||
from .utils.mixplayer import MixPlayer
|
||||
from .utils.paginator import QueuePaginator, TextPaginator, Scroller
|
||||
|
||||
from .utils import checks, thumbnailer, timeformatter
|
||||
from .utils.paginator import QueuePaginator, Scroller, TextPaginator
|
||||
from .utils.selector import Selector
|
||||
|
||||
time_rx = re.compile('[0-9]+')
|
||||
@@ -30,15 +27,6 @@ class Music(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.logger = self.bot.main_logger.bot_logger.getChild("Music")
|
||||
if not hasattr(bot, 'lavalink'): # This ensures the client isn't overwritten during cog reloads.
|
||||
bot.lavalink = lavalink.Client(bot.user.id, player=MixPlayer)
|
||||
|
||||
with codecs.open(f"{self.bot.datadir}/config.yaml", 'r', encoding='utf8') as f:
|
||||
conf = yaml.load(f, Loader=yaml.SafeLoader)
|
||||
|
||||
bot.lavalink.add_node(**conf['lavalink nodes']['main'])
|
||||
self.logger.debug("Adding Lavalink node")
|
||||
bot.add_listener(bot.lavalink.voice_update_handler, 'on_socket_response')
|
||||
|
||||
async def cog_check(self, ctx):
|
||||
if not ctx.guild:
|
||||
@@ -81,11 +69,17 @@ class Music(commands.Cog):
|
||||
numtracks = 0
|
||||
for track in tracks:
|
||||
if track['info']['length'] <= maxlength:
|
||||
thumbnail_url = await thumbnailer.ThumbNailer.identify(
|
||||
self, track['info']['identifier'], track['info']['uri'])
|
||||
track = lavalink.models.AudioTrack(track, ctx.author.id, thumbnail_url=thumbnail_url)
|
||||
player.add(requester=ctx.author.id, track=track)
|
||||
numtracks += 1
|
||||
else:
|
||||
numtracks = len(tracks)
|
||||
for track in tracks:
|
||||
thumbnail_url = await thumbnailer.ThumbNailer.identify(
|
||||
self, track['info']['identifier'], track['info']['uri'])
|
||||
track = lavalink.models.AudioTrack(track, ctx.author.id, thumbnail_url=thumbnail_url)
|
||||
player.add(requester=ctx.author.id, track=track)
|
||||
|
||||
embed.title = '{playlist_enqued}'
|
||||
@@ -94,6 +88,9 @@ class Music(commands.Cog):
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
track = results['tracks'][0]
|
||||
thumbnail_url = await thumbnailer.ThumbNailer.identify(
|
||||
self, track['info']['identifier'], track['info']['uri'])
|
||||
track = lavalink.models.AudioTrack(track, ctx.author.id, thumbnail_url=thumbnail_url)
|
||||
await self.enqueue(ctx, track, embed)
|
||||
embed = ctx.localizer.format_embed(embed)
|
||||
await ctx.send(embed=embed)
|
||||
@@ -144,7 +141,7 @@ class Music(commands.Cog):
|
||||
if player.current:
|
||||
song = f'**[{player.current.title}]({player.current.uri})**'
|
||||
embed = discord.Embed(color=ctx.me.color, description=song, title='{now}')
|
||||
thumbnail_url = await RoxUtils.ThumbNailer.identify(self, player.current.identifier, player.current.uri)
|
||||
thumbnail_url = player.current.extra["thumbnail_url"]
|
||||
member = ctx.guild.get_member(player.current.requester)
|
||||
if thumbnail_url:
|
||||
embed.set_thumbnail(url=thumbnail_url)
|
||||
@@ -195,7 +192,8 @@ class Music(commands.Cog):
|
||||
|
||||
player.queue.clear()
|
||||
await player.stop()
|
||||
await ctx.send(ctx.localizer.format_str("{stop}"))
|
||||
embed = discord.Embed(color=ctx.me.color, title='{stop}')
|
||||
await ctx.send(embed=ctx.localizer.format_embed(embed))
|
||||
|
||||
@commands.command(name='now')
|
||||
async def _now(self, ctx):
|
||||
@@ -214,7 +212,7 @@ class Music(commands.Cog):
|
||||
|
||||
member = ctx.guild.get_member(player.current.requester)
|
||||
embed = discord.Embed(color=ctx.me.color, description=song, title='{now}')
|
||||
thumbnail_url = await RoxUtils.ThumbNailer.identify(self, player.current.identifier, player.current.uri)
|
||||
thumbnail_url = player.current.extra["thumbnail_url"]
|
||||
if thumbnail_url:
|
||||
embed.set_thumbnail(url=thumbnail_url)
|
||||
|
||||
@@ -334,7 +332,7 @@ class Music(commands.Cog):
|
||||
# Create a nice embed explaining what happened
|
||||
song = f'**[{moved.title}]({moved.uri})**'
|
||||
embed = discord.Embed(color=ctx.me.color, description=song, title='{moved.moved}')
|
||||
thumbnail_url = await RoxUtils.ThumbNailer.identify(self, moved.identifier, moved.uri)
|
||||
thumbnail_url = moved.extra["thumbnail_url"]
|
||||
member = ctx.guild.get_member(moved.requester)
|
||||
if thumbnail_url:
|
||||
embed.set_thumbnail(url=thumbnail_url)
|
||||
@@ -560,7 +558,7 @@ class Music(commands.Cog):
|
||||
description = ctx.localizer.format_str("{history.current}", _title=track.title, _uri=track.uri,
|
||||
_id=track.requester) + '\n\n'
|
||||
description += ctx.localizer.format_str("{history.previous}", _len=len(history)-1) + '\n'
|
||||
thumb_url = await RoxUtils.ThumbNailer.identify(self, track.identifier, track.uri)
|
||||
thumbnail_url = track.extra["thumbnail_url"]
|
||||
for index, track in enumerate(history[1:], start=1):
|
||||
description += ctx.localizer.format_str("{history.track}", _index=-index, _title=track.title,
|
||||
_uri=track.uri, _id=track.requester) + '\n'
|
||||
@@ -568,8 +566,8 @@ class Music(commands.Cog):
|
||||
embed = discord.Embed(title=ctx.localizer.format_str('{history.title}'), color=ctx.me.color,
|
||||
description=description)
|
||||
|
||||
if thumb_url:
|
||||
embed.set_thumbnail(url=thumb_url)
|
||||
if thumbnail_url:
|
||||
embed.set_thumbnail(url=thumbnail_url)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.cooldown(1, 5, commands.BucketType.guild)
|
||||
@@ -768,9 +766,11 @@ class Music(commands.Cog):
|
||||
raise commands.CommandInvokeError('You need to be in my voicechannel.')
|
||||
|
||||
async def enqueue(self, ctx, track, embed):
|
||||
|
||||
player = self.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
|
||||
if isinstance(track, dict):
|
||||
track = lavalink.models.AudioTrack(track, ctx.author.id)
|
||||
|
||||
maxlength = self.max_track_length(ctx.guild, player)
|
||||
if maxlength and track['info']['length'] > maxlength:
|
||||
embed.description = ctx.localizer.format_str("{enqueue.toolong}",
|
||||
@@ -793,10 +793,10 @@ class Music(commands.Cog):
|
||||
embed.add_field(name="{enqueue.playing_in}", value=f"`{until_play} ({{enqueue.estimated}})`", inline=True)
|
||||
|
||||
embed.title = '{enqueue.enqueued}'
|
||||
thumb_url = await RoxUtils.ThumbNailer.identify(self, track.identifier, track.uri)
|
||||
thumbnail_url = track.extra["thumbnail_url"]
|
||||
|
||||
if thumb_url:
|
||||
embed.set_thumbnail(url=thumb_url)
|
||||
if thumbnail_url:
|
||||
embed.set_thumbnail(url=thumbnail_url)
|
||||
|
||||
duration = timeformatter.format_ms(int(track.duration))
|
||||
embed.description = f'[{track.title}]({track.uri})\n**{duration}**'
|
||||
|
||||
+16
-16
@@ -2,29 +2,18 @@
|
||||
A cog to separate events from regular music commands
|
||||
"""
|
||||
|
||||
import codecs
|
||||
import yaml
|
||||
# Discord Packages
|
||||
import lavalink
|
||||
from discord.ext import commands
|
||||
|
||||
from .utils.mixplayer import MixPlayer
|
||||
import lavalink.events
|
||||
from discord.ext import commands, tasks
|
||||
|
||||
|
||||
class MusicEvents(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
# TODO: maybe only load when Music loads
|
||||
if not hasattr(bot, 'lavalink'): # This ensures the client isn't overwritten during cog reloads.
|
||||
bot.lavalink = lavalink.Client(bot.user.id, player=MixPlayer)
|
||||
|
||||
with codecs.open(f"{self.bot.datadir}/config.yaml", 'r', encoding='utf8') as f:
|
||||
conf = yaml.load(f, Loader=yaml.SafeLoader)
|
||||
|
||||
bot.lavalink.add_node(**conf['lavalink nodes']['main'])
|
||||
bot.add_listener(bot.lavalink.voice_update_handler, 'on_socket_response')
|
||||
|
||||
lavalink.add_event_hook(self.track_hook)
|
||||
self.leave_timer.start()
|
||||
self.logger = self.bot.main_logger.bot_logger.getChild("Errors")
|
||||
bot.lavalink.add_event_hook(self.track_hook)
|
||||
|
||||
def cog_unload(self):
|
||||
self.bot.lavalink._event_hooks.clear()
|
||||
@@ -77,6 +66,17 @@ class MusicEvents(commands.Cog):
|
||||
await player.stop()
|
||||
await self.connect_to(guild.id, None)
|
||||
|
||||
async def leave_check(self):
|
||||
for player_id in self.bot.lavalink.player_manager.players:
|
||||
await self.check_leave_voice(self.bot.get_guild(player_id))
|
||||
|
||||
@tasks.loop(seconds=10.0)
|
||||
async def leave_timer(self):
|
||||
try:
|
||||
await self.leave_check()
|
||||
except Exception as err:
|
||||
self.logger.debug("Error in leave_timer loop.\nTraceback: %s" % (err))
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MusicEvents(bot))
|
||||
@@ -0,0 +1,157 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
import lavalink
|
||||
from discord.ext import commands
|
||||
from lavalink import Node
|
||||
|
||||
import codecs
|
||||
|
||||
import yaml
|
||||
|
||||
# Bot Utilities
|
||||
from cogs.helpformatter import commandhelper
|
||||
from cogs.utils.paginator import Scroller
|
||||
from .utils.mixplayer import MixPlayer
|
||||
|
||||
|
||||
class NodeManager(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.settings = self.bot.settings
|
||||
self.logger = self.bot.main_logger.bot_logger.getChild("NodeManager")
|
||||
|
||||
music_extensions = [
|
||||
'cogs.music',
|
||||
'cogs.musicevents'
|
||||
]
|
||||
|
||||
if not hasattr(bot, 'lavalink'):
|
||||
bot.lavalink = lavalink.Client(bot.user.id, player=MixPlayer)
|
||||
|
||||
self.load_nodes_from_file()
|
||||
|
||||
bot.add_listener(bot.lavalink.voice_update_handler, 'on_socket_response')
|
||||
|
||||
for extension in music_extensions:
|
||||
try:
|
||||
self.logger.debug("Loading extension %s" % extension)
|
||||
self.bot.load_extension(extension)
|
||||
except Exception:
|
||||
self.logger.exception("Loading of extension %s failed" % extension)
|
||||
|
||||
def load_nodes_from_file(self):
|
||||
with codecs.open(f"{self.bot.datadir}/config.yaml", 'r', encoding='utf8') as f:
|
||||
conf = yaml.load(f, Loader=yaml.SafeLoader)
|
||||
|
||||
name_cache = []
|
||||
new_nodes = []
|
||||
|
||||
for node in self.bot.lavalink.node_manager.nodes:
|
||||
name_cache.append(node.name)
|
||||
|
||||
for node in conf['lavalink nodes']:
|
||||
if node['name'] in name_cache:
|
||||
continue
|
||||
|
||||
self.bot.lavalink.add_node(**node)
|
||||
self.logger.debug("Adding Lavalink node: %s on %s with the port %s in %s" % (
|
||||
node['name'], node['host'],
|
||||
node['port'], node['region'],))
|
||||
new_nodes.append({**node})
|
||||
return new_nodes
|
||||
|
||||
async def _regioner(self, region):
|
||||
flags = {
|
||||
'us': ':flag_us:',
|
||||
'eu': ':flag_eu:',
|
||||
'singapore': ':flag_sg:',
|
||||
'london': ':flag_gb:',
|
||||
'sydney': ':flag_au:',
|
||||
'amsterdam': ':flag_nl:',
|
||||
'frankfurt': ':flag_de:',
|
||||
'brazil': ':flag_br:',
|
||||
'japan': ':flag_jp:',
|
||||
'russia': ':flag_ru:',
|
||||
'southafrica': ':flag_za:',
|
||||
'hongkong': ':flag_hk:',
|
||||
'india': ':flag_in:'
|
||||
}
|
||||
try:
|
||||
return flags[region]
|
||||
except KeyError:
|
||||
return ':question:'
|
||||
|
||||
async def _node_presenter(self, ctx, node):
|
||||
embed = discord.Embed(color=ctx.me.color)
|
||||
embed.title = 'Added new node!'
|
||||
|
||||
if isinstance(node, list):
|
||||
for n in node:
|
||||
embed.add_field(name=f'{await self._regioner(n.region)} **Name:** {n.name}',
|
||||
value=f'**Host:** {n.host}\n **Port:** {n.port}')
|
||||
|
||||
if isinstance(node, Node):
|
||||
embed.add_field(name=f'{await self._regioner(node.region)} **Name:** {node.name}',
|
||||
value=f'**Host:** {node.host}\n **Port:** {node.port}')
|
||||
|
||||
if isinstance(node, dict):
|
||||
embed.description = f'**Name:** {node.get("name")}\n **Host:** {node.get("host")}\n ' \
|
||||
f'**Port:** {node.get("port")}\n **Region:** {await self._regioner(node.get("region"))}'
|
||||
|
||||
return embed
|
||||
|
||||
@commands.group(name='node', hidden=True)
|
||||
@commands.is_owner()
|
||||
async def _node(self, ctx):
|
||||
if ctx.invoked_subcommand is None:
|
||||
paginator = commandhelper(ctx, ctx.command, ctx.invoker, include_subcmd=True)
|
||||
scroller = Scroller(ctx, paginator)
|
||||
await scroller.start_scrolling()
|
||||
|
||||
@_node.command(name='reload_file')
|
||||
@commands.is_owner()
|
||||
async def reload_file(self, ctx):
|
||||
for node in self.load_nodes_from_file():
|
||||
embed = await self._node_presenter(ctx, node)
|
||||
embed.title = 'Added new node from file!'
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@_node.command(name='add')
|
||||
@commands.is_owner()
|
||||
async def _add(self, ctx, host, port, password, region, name=None):
|
||||
self.bot.lavalink.add_node(host, port, password, region, name)
|
||||
self.logger.debug("Adding Lavalink node: %s on %s with the port %s in %s" % (host, port, region, name,))
|
||||
embed = await self._node_presenter(ctx, {'host': host, 'port': port, 'password': password,
|
||||
'region': region, 'name': name})
|
||||
embed.title = 'Added new node!'
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@_node.command(name='list')
|
||||
@commands.is_owner()
|
||||
async def list_nodes(self, ctx):
|
||||
embed = await self._node_presenter(ctx, self.bot.lavalink.node_manager.nodes)
|
||||
embed.title = 'Lavalink nodes attatched to this bot:'
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@_node.command(name='remove')
|
||||
@commands.is_owner()
|
||||
async def _remove(self, ctx, node):
|
||||
sent_feedback = False
|
||||
for _node in self.bot.lavalink.node_manager.nodes:
|
||||
if len(self.bot.lavalink.node_manager.nodes) <= 1:
|
||||
await ctx.send('Cannot remove the last node')
|
||||
sent_feedback = True
|
||||
break
|
||||
if (_node.name or _node.host) == node:
|
||||
sent_feedback = True
|
||||
embed = await self._node_presenter(ctx, _node)
|
||||
embed.title = 'Removed node from bot'
|
||||
await ctx.send(embed=embed)
|
||||
self.bot.lavalink.node_manager.remove_node(_node)
|
||||
self.logger.info("Removed Lavalink node: %s" % _node.host)
|
||||
if not sent_feedback:
|
||||
await ctx.send('No node found')
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(NodeManager(bot))
|
||||
+5
-3
@@ -1,6 +1,8 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
# Bot Utilities
|
||||
from cogs.utils import checks
|
||||
|
||||
|
||||
@@ -188,9 +190,9 @@ class Settings(commands.Cog):
|
||||
|
||||
embed = discord.Embed(title='Max duration type set to', color=ctx.me.color)
|
||||
if is_dynamic:
|
||||
embed.description = f'Dynamic'
|
||||
embed.description = 'Dynamic'
|
||||
else:
|
||||
embed.description = f'Static'
|
||||
embed.description = 'Static'
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.guild_only()
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
import yaml
|
||||
# Discord Packages
|
||||
from discord.ext import commands
|
||||
|
||||
from glob import glob
|
||||
from os import path
|
||||
|
||||
from discord.ext import commands
|
||||
import yaml
|
||||
|
||||
"""
|
||||
Not the prettiest this, works by replacing any found aliases in a command string with the actual command names.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
@@ -60,7 +61,7 @@ def is_dj(ctx):
|
||||
def dj_or(alone: bool = False, track_requester: bool = False):
|
||||
async def predicate(ctx):
|
||||
try:
|
||||
player = ctx.bot.lavalink.players.get(ctx.guild.id)
|
||||
player = ctx.bot.lavalink.player_manager.get(ctx.guild.id)
|
||||
is_alone = (ctx.author in player.listeners and len(player.listeners) == 1) and alone
|
||||
requester = (player.current.requester == ctx.author.id) and track_requester
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Discord Packages
|
||||
from discord.ext import commands
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# Discord Packages
|
||||
from discord import Embed
|
||||
|
||||
import copy
|
||||
import re
|
||||
from glob import glob
|
||||
from os import path
|
||||
|
||||
import re
|
||||
|
||||
from cogs.utils.dict_utils import flatten, SafeDict
|
||||
|
||||
import yaml
|
||||
|
||||
import copy
|
||||
from discord import Embed
|
||||
# Bot Utilities
|
||||
from cogs.utils.dict_utils import SafeDict, flatten
|
||||
|
||||
"""
|
||||
Localizer for bot
|
||||
@@ -95,6 +96,7 @@ class Localizer:
|
||||
nstr = nstr.replace(".", "/")
|
||||
value = value.replace(outer, nstr)
|
||||
return value
|
||||
|
||||
# parses and interpolates strings
|
||||
@staticmethod
|
||||
def _parse_localization_string(value, d, prefix=None):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import logging.handlers
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# Discord Packages
|
||||
import lavalink
|
||||
from lavalink import AudioTrack, DefaultPlayer, Node
|
||||
from lavalink.events import QueueEndEvent, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent
|
||||
|
||||
import typing
|
||||
from collections import OrderedDict, deque
|
||||
from itertools import cycle, islice, chain
|
||||
from itertools import chain, cycle, islice
|
||||
from random import shuffle
|
||||
|
||||
from lavalink.events import TrackStartEvent, TrackStuckEvent, TrackExceptionEvent, TrackEndEvent, QueueEndEvent
|
||||
from lavalink import Node, DefaultPlayer, AudioTrack
|
||||
|
||||
|
||||
class MixPlayer(DefaultPlayer):
|
||||
def __init__(self, guild_id: int, node: Node):
|
||||
@@ -18,13 +19,13 @@ class MixPlayer(DefaultPlayer):
|
||||
self.skip_voters = set()
|
||||
self.boosted = False
|
||||
|
||||
def add(self, requester: int, track: dict, pos: int = None):
|
||||
def add(self, requester: int, track: typing.Union[dict, AudioTrack], pos: int = None):
|
||||
""" Adds a track to the queue. """
|
||||
return self.queue.add_track(requester, AudioTrack(track, requester), pos)
|
||||
return self.queue.add_track(requester, track, pos)
|
||||
|
||||
def add_next(self, requester: int, track: dict):
|
||||
def add_next(self, requester: int, track: typing.Union[dict, AudioTrack], pos: int = None):
|
||||
""" Adds a track to beginning of the queue """
|
||||
self.queue.add_next_track(AudioTrack(track, requester))
|
||||
self.queue.add_next_track(track)
|
||||
|
||||
def move_user_track(self, requester: int, initial: int, final: int):
|
||||
""" Moves a track in a users queue"""
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
|
||||
import asyncio
|
||||
|
||||
|
||||
@@ -93,7 +95,7 @@ class FieldPaginator(BasePaginator):
|
||||
field = {"name": name, "value": value}
|
||||
fieldsize = sum([len(val) for _, val in field.items()])
|
||||
if len(name) > 256 or len(value) > 1024:
|
||||
raise RuntimeError(f'Maximum field size exceeded, max name length: 256. max value length: 1024.')
|
||||
raise RuntimeError('Maximum field size exceeded, max name length: 256. max value length: 1024.')
|
||||
|
||||
if self._current_page_size + fieldsize > self._max_size:
|
||||
self.close_page()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# Discord Packages
|
||||
import discord
|
||||
|
||||
import asyncio
|
||||
import inspect
|
||||
|
||||
from .paginator import TextPaginator
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import codecs
|
||||
import locale as localee
|
||||
import os
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class ThumbNailer(object):
|
||||
img = soup.find("meta", property="twitter:image")
|
||||
return img["content"]
|
||||
except Exception as e:
|
||||
self.logger.exception("%s".format(e))
|
||||
self.logger.exception("%s" % e)
|
||||
|
||||
async def _bandcamp(self, url):
|
||||
html = await ThumbNailer.get_html(self, url)
|
||||
@@ -28,7 +28,7 @@ class ThumbNailer(object):
|
||||
img = soup.find(class_="popupImage").get("href")
|
||||
return img
|
||||
except Exception as e:
|
||||
self.logger.exception("%s".format(e))
|
||||
self.logger.exception("%s" % e)
|
||||
|
||||
async def _vimeo(self, url):
|
||||
html = await ThumbNailer.get_html(self, url)
|
||||
@@ -37,7 +37,7 @@ class ThumbNailer(object):
|
||||
img = soup.find("meta", property="og:image")
|
||||
return img["content"]
|
||||
except Exception as e:
|
||||
self.logger.exception("%s".format(e))
|
||||
self.logger.exception("%s" % e)
|
||||
|
||||
@staticmethod
|
||||
async def identify(self, identifier, uri):
|
||||
@@ -19,20 +19,17 @@ default server settings:
|
||||
dynamic max duration: Yes
|
||||
|
||||
lavalink nodes:
|
||||
main: # The preferred lavalink node for your music bot
|
||||
host: localhost
|
||||
- host: localhost
|
||||
port: 2333
|
||||
password: youshallnotpass
|
||||
region: eu
|
||||
name: default-node
|
||||
name: main
|
||||
|
||||
# Backup nodes for your music bot
|
||||
backup:
|
||||
- host: localhost
|
||||
port: 2333
|
||||
password: youshallnotpass
|
||||
region: eu
|
||||
name: backup-1
|
||||
- host: localhost
|
||||
port: 2333
|
||||
password: youshallnotpass
|
||||
region: eu
|
||||
name: backup-1
|
||||
|
||||
APIkeys:
|
||||
genius:
|
||||
|
||||
@@ -112,6 +112,24 @@ reloadalias:
|
||||
aliases: [reloadalias]
|
||||
description: 'Reload all aliases and help information.'
|
||||
|
||||
# NodeManager
|
||||
node:
|
||||
aliases: [node]
|
||||
description: 'Manage server nodes.'
|
||||
sub_commands:
|
||||
add:
|
||||
aliases: [add]
|
||||
description: 'Adds node to pool.'
|
||||
list:
|
||||
aliases: [list, nodes]
|
||||
description: 'Lists nodes in the pool.'
|
||||
remove:
|
||||
aliases: [remove]
|
||||
description: 'Removes nodes in the pool.'
|
||||
reload_file:
|
||||
aliases: [reload_file, reload]
|
||||
description: 'Reads the settings and adds newly added nodes.'
|
||||
|
||||
# Settings
|
||||
settings:
|
||||
aliases: [settings, set]
|
||||
|
||||
@@ -15,7 +15,7 @@ skip_to:
|
||||
invalid_pos: Invalid specified position.
|
||||
exceeds_queue: The position exceeds the queue\'s length.
|
||||
skipped_to: skipped to `{_title}` at position `{_pos}`
|
||||
stop: "⏹ | Stopped."
|
||||
stop: "Stopped."
|
||||
live: "\U0001F534 LIVE"
|
||||
now: Now Playing
|
||||
queue:
|
||||
@@ -74,3 +74,10 @@ history:
|
||||
settings_check:
|
||||
textchannel: "You can't use music commands in this channel.\nTry in: "
|
||||
voicechannel: 'I can only play music in: '
|
||||
errors:
|
||||
error_occurred: An error occurred
|
||||
join_voice_first: You need to join a voicechannel first
|
||||
not_connected: Not connected
|
||||
need_permission: I need the `CONNECT` and `SPEAK` permissions
|
||||
right_channel: You need to be in the right voice channel
|
||||
my_channel: You need to be in my voicechannel
|
||||
@@ -0,0 +1,2 @@
|
||||
pageindicator: Viewing page {_current}/{_total} | Type "{_prefix}help <command>" for more information
|
||||
notcommand: Looks like "{_command}" is not a command or category.
|
||||
@@ -74,3 +74,10 @@ history:
|
||||
settings_check:
|
||||
textchannel: "Du kan ikke bruke musikkommandoer i denne kanalen.\nPrøv i: "
|
||||
voicechannel: 'Jeg kan bare spille musikk i: '
|
||||
errors:
|
||||
error_occurred: En feil oppstod
|
||||
join_voice_first: Du må være i en stemmekanal
|
||||
not_connected: Ikke tilkoblet
|
||||
need_permission: Jeg trenger tillgang til `CONNECT` og `SPEAK`
|
||||
right_channel: Du må være i riktig kanal
|
||||
my_channel: Du må være i samme stemmekanal som meg
|
||||
@@ -0,0 +1,2 @@
|
||||
pageindicator: Viser side {_current}/{_total} | Skriv "{_prefix}hjelp <kommando>" for mer informasjon
|
||||
notcommand: Ser ut som "{_command}" ikke er en kategori eller en kommando.
|
||||
+2
-3
@@ -1,6 +1,5 @@
|
||||
discord.py >= 1.3
|
||||
git+https://github.com/Devoxin/Lavalink.py@b614a5389e3cf5ea7c444041428a05b084ca15ec
|
||||
discord.py
|
||||
git+https://github.com/Devoxin/Lavalink.py@dev
|
||||
asyncio
|
||||
pyyaml
|
||||
BeautifulSoup4
|
||||
aiohttp<3.5.0,>=3.3.0
|
||||
@@ -0,0 +1,26 @@
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
count = True
|
||||
statistics = True
|
||||
|
||||
[isort]
|
||||
known_discord = discord,lavalink
|
||||
known_utils = cogs.utils,cogs.helpformatter
|
||||
import_heading_discord = Discord Packages
|
||||
import_heading_utils = Bot Utilities
|
||||
sections = DISCORD,FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,UTILS,LOCALFOLDER
|
||||
no_lines_before = LOCALFOLDER
|
||||
indent = " "
|
||||
line_length = 120
|
||||
balanced_wrapping = True
|
||||
multi_line_output = 4
|
||||
|
||||
[pycodestyle]
|
||||
max_line_length = 120
|
||||
|
||||
[pylint.FORMAT]
|
||||
max-line-length=120
|
||||
|
||||
[pylint.BASIC]
|
||||
variable-naming-style=camelCase
|
||||
constant-naming-style=camelCase
|
||||
Reference in new issue
Block a user