Merge pull request #28 from r-Norge/dev

Update Master
This commit is contained in:
Ev-1 authored and GitHub committed 2020-08-28 16:12:47 +02:00
commit 8e6c3e7703
33 files changed
+561 -163

No files matched your search

+73
View File
@@ -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
+7
View File
@@ -0,0 +1,7 @@
{
"recommendations": [
"oderwat.indent-rainbow",
"ms-python.python",
"ms-python.vscode-pylance"
]
}
+15
View File
@@ -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"
}]
}
+15
View File
@@ -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
View File
@@ -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
+11 -9
View File
@@ -1,9 +1,10 @@
# WIP - Shite Music Bot
[![Build Status](https://jenkins.roxedus.net/buildStatus/icon?job=MusicBot%2Fdev)](https://jenkins.roxedus.net/job/MusicBot/job/dev/)
[![](https://images.microbadger.com/badges/image/rnorge/music:dev-amd64.svg)](https://microbadger.com/images/rnorge/music:dev-amd64 "Get your own image badge on microbadger.com")
[![MicroBadger](https://images.microbadger.com/badges/image/rnorge/music:dev-amd64.svg)](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
````
````
+18 -53
View File
@@ -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
View File
@@ -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()
+83
View File
@@ -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))
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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))
+157
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+2 -1
View File
@@ -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
View File
@@ -1,3 +1,4 @@
# Discord Packages
from discord.ext import commands
+8 -6
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
import logging.handlers
import logging
import logging.handlers
import os
+9 -8
View File
@@ -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"""
+3 -1
View File
@@ -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()
+3
View File
@@ -1,6 +1,9 @@
# Discord Packages
import discord
import asyncio
import inspect
from .paginator import TextPaginator
+2 -1
View File
@@ -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):
+7 -10
View File
@@ -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:
+18
View File
@@ -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]
+8 -1
View File
@@ -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
+2
View File
@@ -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.
+7
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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
+26
View File
@@ -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