Second wave of initial code changes.

This commit is contained in:
Storm Dragon 2025-06-13 18:53:38 -04:00
parent b1e21af243
commit de270bc842
22 changed files with 120 additions and 318 deletions

View File

@ -1,12 +1,12 @@
ARG ARCH= ARG ARCH=
FROM python:3.11-slim-bullseye AS python-builder FROM python:3.11-slim-bullseye AS python-builder
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /botamusique WORKDIR /bragi
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends -y gcc g++ ffmpeg libjpeg-dev libmagic-dev opus-tools zlib1g-dev \ && apt-get install --no-install-recommends -y gcc g++ ffmpeg libjpeg-dev libmagic-dev opus-tools zlib1g-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY . /botamusique COPY . /bragi
RUN python3 -m venv venv \ RUN python3 -m venv venv \
&& venv/bin/pip install wheel \ && venv/bin/pip install wheel \
&& venv/bin/pip install -r requirements.txt && venv/bin/pip install -r requirements.txt
@ -17,9 +17,9 @@ EXPOSE 8181
RUN apt update && \ RUN apt update && \
apt install --no-install-recommends -y opus-tools ffmpeg libmagic-dev curl tar && \ apt install --no-install-recommends -y opus-tools ffmpeg libmagic-dev curl tar && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
COPY --from=python-builder /botamusique /botamusique COPY --from=python-builder /bragi /bragi
WORKDIR /botamusique WORKDIR /bragi
RUN chmod +x entrypoint.sh RUN chmod +x entrypoint.sh
ENTRYPOINT [ "/botamusique/entrypoint.sh" ] ENTRYPOINT [ "/bragi/entrypoint.sh" ]
CMD ["venv/bin/python", "mumbleBot.py"] CMD ["venv/bin/python", "mumbleBot.py"]

View File

@ -3,18 +3,18 @@ ARG ARCH=
FROM ${ARCH}python:3-slim-bullseye AS source FROM ${ARCH}python:3-slim-bullseye AS source
ARG VERSION=master ARG VERSION=master
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /botamusique WORKDIR /bragi
RUN apt-get update && apt-get install -y git RUN apt-get update && apt-get install -y git
RUN git clone --recurse-submodules https://github.com/azlux/botamusique.git . && git checkout $VERSION RUN git clone --recurse-submodules https://github.com/azlux/bragi.git . && git checkout $VERSION
FROM ${ARCH}python:3-slim-bullseye AS python-builder FROM ${ARCH}python:3-slim-bullseye AS python-builder
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /botamusique WORKDIR /bragi
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y gcc ffmpeg libjpeg-dev libmagic-dev opus-tools zlib1g-dev \ && apt-get install -y gcc ffmpeg libjpeg-dev libmagic-dev opus-tools zlib1g-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY --from=source /botamusique . COPY --from=source /bragi .
RUN python3 -m venv venv \ RUN python3 -m venv venv \
&& venv/bin/pip install wheel \ && venv/bin/pip install wheel \
&& venv/bin/pip install -r requirements.txt && venv/bin/pip install -r requirements.txt
@ -22,30 +22,30 @@ RUN python3 -m venv venv \
FROM ${ARCH}node:14-bullseye-slim AS node-builder FROM ${ARCH}node:14-bullseye-slim AS node-builder
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /botamusique/web WORKDIR /bragi/web
COPY --from=source /botamusique/web . COPY --from=source /bragi/web .
RUN npm install RUN npm install
RUN npm run build RUN npm run build
FROM ${ARCH}python:3-slim-bullseye AS template-builder FROM ${ARCH}python:3-slim-bullseye AS template-builder
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /botamusique WORKDIR /bragi
COPY --from=python-builder /botamusique . COPY --from=python-builder /bragi .
COPY --from=node-builder /botamusique/templates templates COPY --from=node-builder /bragi/templates templates
RUN venv/bin/python scripts/translate_templates.py --lang-dir /botamusique/lang --template-dir /botamusique/web/templates RUN venv/bin/python scripts/translate_templates.py --lang-dir /bragi/lang --template-dir /bragi/web/templates
FROM ${ARCH}python:3-slim-bullseye FROM ${ARCH}python:3-slim-bullseye
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
EXPOSE 8181 EXPOSE 8181
WORKDIR /botamusique WORKDIR /bragi
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y ffmpeg libmagic-dev opus-tools zlib1g \ && apt-get install -y ffmpeg libmagic-dev opus-tools zlib1g \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY --from=python-builder /botamusique . COPY --from=python-builder /bragi .
COPY --from=node-builder /botamusique/static static COPY --from=node-builder /bragi/static static
COPY --from=template-builder /botamusique/templates templates COPY --from=template-builder /bragi/templates templates
RUN chmod +x entrypoint.sh RUN chmod +x entrypoint.sh
ENTRYPOINT [ "/botamusique/entrypoint.sh" ] ENTRYPOINT [ "/bragi/entrypoint.sh" ]
CMD ["/botamusique/venv/bin/python", "/botamusique/mumbleBot.py"] CMD ["/bragi/venv/bin/python", "/bragi/mumbleBot.py"]

View File

@ -1,4 +1,8 @@
# coding=utf-8 # coding=utf-8
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import logging import logging
import secrets import secrets
import datetime import datetime
@ -8,7 +12,6 @@ import pymumble_py3 as pymumble
from constants import tr_cli as tr from constants import tr_cli as tr
from constants import commands from constants import commands
import interface
import util import util
import variables as var import variables as var
from pyradios import RadioBrowser from pyradios import RadioBrowser
@ -53,7 +56,6 @@ def register_all_commands(bot):
bot.register_command(commands('remove'), cmd_remove) bot.register_command(commands('remove'), cmd_remove)
bot.register_command(commands('remove_tag'), cmd_remove_tag) bot.register_command(commands('remove_tag'), cmd_remove_tag)
bot.register_command(commands('repeat'), cmd_repeat) bot.register_command(commands('repeat'), cmd_repeat)
bot.register_command(commands('requests_webinterface_access'), cmd_web_access)
bot.register_command(commands('rescan'), cmd_refresh_cache, no_partial_match=True) bot.register_command(commands('rescan'), cmd_refresh_cache, no_partial_match=True)
bot.register_command(commands('search'), cmd_search_library) bot.register_command(commands('search'), cmd_search_library)
bot.register_command(commands('skip'), cmd_skip) bot.register_command(commands('skip'), cmd_skip)
@ -65,13 +67,9 @@ def register_all_commands(bot):
bot.register_command(commands('yt_search'), cmd_yt_search) bot.register_command(commands('yt_search'), cmd_yt_search)
# admin command # admin command
bot.register_command(commands('add_webinterface_user'), cmd_web_user_add, admin=True)
bot.register_command(commands('drop_database'), cmd_drop_database, no_partial_match=True, admin=True) bot.register_command(commands('drop_database'), cmd_drop_database, no_partial_match=True, admin=True)
bot.register_command(commands('kill'), cmd_kill, admin=True) bot.register_command(commands('kill'), cmd_kill, admin=True)
bot.register_command(commands('list_webinterface_user'), cmd_web_user_list, admin=True)
bot.register_command(commands('remove_webinterface_user'), cmd_web_user_remove, admin=True)
bot.register_command(commands('max_volume'), cmd_max_volume, admin=True) bot.register_command(commands('max_volume'), cmd_max_volume, admin=True)
bot.register_command(commands('update'), cmd_update, no_partial_match=True, admin=True)
bot.register_command(commands('url_ban'), cmd_url_ban, no_partial_match=True, admin=True) bot.register_command(commands('url_ban'), cmd_url_ban, no_partial_match=True, admin=True)
bot.register_command(commands('url_ban_list'), cmd_url_ban_list, no_partial_match=True, admin=True) bot.register_command(commands('url_ban_list'), cmd_url_ban_list, no_partial_match=True, admin=True)
bot.register_command(commands('url_unban'), cmd_url_unban, no_partial_match=True, admin=True) bot.register_command(commands('url_unban'), cmd_url_unban, no_partial_match=True, admin=True)
@ -647,17 +645,6 @@ def cmd_kill(bot, user, text, command, parameter):
bot.exit = True bot.exit = True
def cmd_update(bot, user, text, command, parameter):
global log
if bot.is_admin(user):
bot.mumble.users[text.actor].send_text_message(
tr('start_updating'))
msg = util.update(bot.version)
bot.mumble.users[text.actor].send_text_message(msg)
else:
bot.mumble.users[text.actor].send_text_message(
tr('not_admin'))
def cmd_stop_and_getout(bot, user, text, command, parameter): def cmd_stop_and_getout(bot, user, text, command, parameter):
@ -1249,88 +1236,6 @@ def cmd_refresh_cache(bot, user, text, command, parameter):
bot.mumble.users[text.actor].send_text_message(tr('not_admin')) bot.mumble.users[text.actor].send_text_message(tr('not_admin'))
def cmd_web_access(bot, user, text, command, parameter):
auth_method = var.config.get("webinterface", "auth_method")
if auth_method == 'token':
interface.banned_ip = []
interface.bad_access_count = {}
user_info = var.db.get("user", user, fallback='{}')
user_dict = json.loads(user_info)
if 'token' in user_dict:
var.db.remove_option("web_token", user_dict['token'])
token = secrets.token_urlsafe(5)
user_dict['token'] = token
user_dict['token_created'] = str(datetime.datetime.now())
user_dict['last_ip'] = ''
var.db.set("web_token", token, user)
var.db.set("user", user, json.dumps(user_dict))
access_address = var.config.get("webinterface", "access_address") + "/?token=" + token
else:
access_address = var.config.get("webinterface", "access_address")
bot.send_msg(tr('webpage_address', address=access_address), text)
def cmd_user_password(bot, user, text, command, parameter):
if not parameter:
bot.send_msg(tr('bad_parameter', command=command), text)
return
user_info = var.db.get("user", user, fallback='{}')
user_dict = json.loads(user_info)
user_dict['password'], user_dict['salt'] = util.get_salted_password_hash(parameter)
var.db.set("user", user, json.dumps(user_dict))
bot.send_msg(tr('user_password_set'), text)
def cmd_web_user_add(bot, user, text, command, parameter):
if not parameter:
bot.send_msg(tr('bad_parameter', command=command), text)
return
auth_method = var.config.get("webinterface", "auth_method")
if auth_method == 'password':
web_users = json.loads(var.db.get("privilege", "web_access", fallback='[]'))
if parameter not in web_users:
web_users.append(parameter)
var.db.set("privilege", "web_access", json.dumps(web_users))
bot.send_msg(tr('web_user_list', users=", ".join(web_users)), text)
else:
bot.send_msg(tr('command_disabled', command=command), text)
def cmd_web_user_remove(bot, user, text, command, parameter):
if not parameter:
bot.send_msg(tr('bad_parameter', command=command), text)
return
auth_method = var.config.get("webinterface", "auth_method")
if auth_method == 'password':
web_users = json.loads(var.db.get("privilege", "web_access", fallback='[]'))
if parameter in web_users:
web_users.remove(parameter)
var.db.set("privilege", "web_access", json.dumps(web_users))
bot.send_msg(tr('web_user_list', users=", ".join(web_users)), text)
else:
bot.send_msg(tr('command_disabled', command=command), text)
def cmd_web_user_list(bot, user, text, command, parameter):
auth_method = var.config.get("webinterface", "auth_method")
if auth_method == 'password':
web_users = json.loads(var.db.get("privilege", "web_access", fallback='[]'))
bot.send_msg(tr('web_user_list', users=", ".join(web_users)), text)
else:
bot.send_msg(tr('command_disabled', command=command), text)
def cmd_version(bot, user, text, command, parameter): def cmd_version(bot, user, text, command, parameter):

View File

@ -1,5 +1,6 @@
# ======================================================== # ========================================================
# botamusique Default Configuration File # Bragi Default Configuration File
# Forked from botamusique by azlux
# Version 6 # Version 6
# ======================================================== # ========================================================
# WARNING: # WARNING:
@ -31,7 +32,6 @@ admin =
allow_other_channel_message = False allow_other_channel_message = False
allow_private_message = True allow_private_message = True
announce_current_music = True announce_current_music = True
auto_check_update = True
autoplay_length = 5 autoplay_length = 5
avatar = avatar =
bandwidth = 96000 bandwidth = 96000
@ -59,28 +59,13 @@ refresh_cache_on_startup = True
save_music_library = True save_music_library = True
save_playlist = True save_playlist = True
stereo = True stereo = True
target_version = git
tmp_folder = /tmp/ tmp_folder = /tmp/
tmp_folder_max_size = 10 tmp_folder_max_size = 10
username = botamusique username = bragi
volume = 0.8 volume = 0.8
when_nobody_in_channel = nothing when_nobody_in_channel = nothing
when_nobody_in_channel_ignore = when_nobody_in_channel_ignore =
[webinterface]
access_address = http://127.0.0.1:8181
auth_method = none
enabled = False
flask_secret = ChangeThisPassword
is_web_proxified = True
listening_addr = 127.0.0.1
listening_port = 8181
max_attempts = 10
max_upload_file_size = 30M
password =
upload_enabled = True
user =
web_logfile =
[debug] [debug]
ffmpeg = False ffmpeg = False
@ -102,7 +87,6 @@ user_agent =
[commands] [commands]
add_from_shortlist = shortlist, sl add_from_shortlist = shortlist, sl
add_tag = addtag add_tag = addtag
add_webinterface_user = webuseradd
change_user_password = password change_user_password = password
clear = clear clear = clear
command_symbol = !: command_symbol = !:
@ -118,7 +102,6 @@ joinme = joinme
kill = kill kill = kill
last = last last = last
list_file = listfile list_file = listfile
list_webinterface_user = webuserlist
max_volume = maxvolume max_volume = maxvolume
mode = mode mode = mode
pause = pause pause = pause
@ -135,16 +118,13 @@ rb_play = rbplay
rb_query = rbquery rb_query = rbquery
remove = rm remove = rm
remove_tag = untag remove_tag = untag
remove_webinterface_user = webuserdel
repeat = repeat repeat = repeat
requests_webinterface_access = web
rescan = rescan rescan = rescan
search = search search = search
skip = skip skip = skip
split_username_at_space = False split_username_at_space = False
stop = stop stop = stop
stop_and_getout = oust stop_and_getout = oust
update = update
url_ban = urlban url_ban = urlban
url_ban_list = urlbanlist url_ban_list = urlbanlist
url_unban = urlunban url_unban = urlunban

View File

@ -1,5 +1,6 @@
# ======================================================== # ========================================================
# botamusique example configuration file # Bragi example configuration file
# Forked from botamusique by azlux
# Version 6 # Version 6
# ======================================================== # ========================================================
# Rename this file to configuration.ini after editing. # Rename this file to configuration.ini after editing.
@ -22,7 +23,7 @@ port = 64738
# 'username': The bot's username. # 'username': The bot's username.
# 'comment': Comment displayed on the bot's profile. # 'comment': Comment displayed on the bot's profile.
# 'avatar': Path to an image used for the bot's avatar (PNG recommended, 128 KB max). # 'avatar': Path to an image used for the bot's avatar (PNG recommended, 128 KB max).
#username = botamusique #username = bragi
#comment = "Hi, I'm here to play radio, local music or youtube/soundcloud music. Have fun!" #comment = "Hi, I'm here to play radio, local music or youtube/soundcloud music. Have fun!"
#avatar = #avatar =
@ -155,46 +156,7 @@ port = 64738
# query youtube", you should provide a value here. # query youtube", you should provide a value here.
#youtube_query_cookie = {"CONSENT": "paste your CONSENT cookie value here"} #youtube_query_cookie = {"CONSENT": "paste your CONSENT cookie value here"}
# The [webinterface] section stores settings related to the web interface. # Web interface has been removed from Bragi
[webinterface]
# 'enabled': Whether to enable the web interface to allow managing your playlist,
# uploading tracks, etc.
# The web interface is disabled by default for security and performance reasons.
# 'access_address': URL provided to users when the public URL for the
# web interface is requested.
#enabled = False
#listening_addr = 127.0.0.1
#listening_port = 8181
#is_web_proxified = True
#access_address = http://127.0.0.1:8181
# 'web_logfile': If this is provided, web server access logs are written to this file.
#web_logfile =
# 'auth_method': Method used to authenticate users accessing the web interface.
# One of 'none', 'password' or 'token'. If this is set to 'token', a unique token
# is used for authentication.
# 'max_attempts': Amount of incorrect login attempts needed before being banned.
# Regenerating a token or rebooting the bot will reset this number.
#auth_method = token
#max_attempts = 10
# 'user', 'password': If auth_method is set to 'password', you'll need to set
# the default username and password, which is set by these two options.
# You can add more users using the '!webadduser' command.
#user = botamusique
#password = mumble
# 'flask_secret': To use a token, Flask needs a password to encrypt/sign cookies.
# This is absolutely necessary if auth_method is 'token'!
#flask_secret = ChangeThisPassword
# 'upload_enabled': Whether to enable the upload function of the web interface.
# If this is False, only admins can upload files.
# 'maximum_upload_file_size': Maximum file size allowed for uploads.
# Can be specified in B, KB, MB, GB, or TB.
#upload_enabled = True
#max_upload_file_size = 30MB
# The [debug] section contains settings to enable debugging messaages. # The [debug] section contains settings to enable debugging messaages.
[debug] [debug]

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import os import os
import json import json

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import os import os
import re import re
import sqlite3 import sqlite3

View File

@ -51,14 +51,14 @@ fi
if [ -n "$BAM_CONFIG_file" ]; then if [ -n "$BAM_CONFIG_file" ]; then
if [ ! -f "$BAM_CONFIG_file" ]; then if [ ! -f "$BAM_CONFIG_file" ]; then
cp "/botamusique/configuration.example.ini" "$BAM_CONFIG_file" cp "/bragi/configuration.example.ini" "$BAM_CONFIG_file"
fi fi
command+=( "--config" "$BAM_CONFIG_file" ) command+=( "--config" "$BAM_CONFIG_file" )
else else
if [ ! -f "/botamusique/configuration.ini" ]; then if [ ! -f "/bragi/configuration.ini" ]; then
cp "/botamusique/configuration.example.ini" "/botamusique/configuration.ini" cp "/bragi/configuration.example.ini" "/bragi/configuration.ini"
fi fi
command+=( "--config" "/botamusique/configuration.ini" ) command+=( "--config" "/bragi/configuration.ini" )
fi fi
exec "${command[@]}" exec "${command[@]}"

View File

@ -36,7 +36,7 @@
"multiple_file_deleted": "Multiple items deleted from the library:", "multiple_file_deleted": "Multiple items deleted from the library:",
"multiple_file_found": "Found:", "multiple_file_found": "Found:",
"multiple_matches": "File not found! Possible candidates:", "multiple_matches": "File not found! Possible candidates:",
"new_version_found": "<h2>Update Available!</h2> Version {new_version} of botamusique is available! <hr />\n<h3>Changelog</h3> {changelog} <hr /> Send <i>!update</i> to update!", "new_version_found": "<h2>Update Available!</h2> Version {new_version} of bragi is available! <hr />\n<h3>Changelog</h3> {changelog} <hr /> Send <i>!update</i> to update!",
"next_to_play": "Next song.", "next_to_play": "Next song.",
"no_file": "File not found.", "no_file": "File not found.",
"not_admin": "You are not an admin!", "not_admin": "You are not an admin!",
@ -60,7 +60,7 @@
"removed_tags_from_all": "Removed tags <i>{tags}</i> from songs on the playlist.", "removed_tags_from_all": "Removed tags <i>{tags}</i> from songs on the playlist.",
"removing_item": "Removed entry {item} from playlist.", "removing_item": "Removed entry {item} from playlist.",
"repeat": "Repeat {song} for {n} times.", "repeat": "Repeat {song} for {n} times.",
"report_version": "The current version of botamusique is <b>{version}</b>.", "report_version": "The current version of bragi is <b>{version}</b>.",
"shortlist_instruction": "Use <i>!sl {indexes}</i> to play the item you want.", "shortlist_instruction": "Use <i>!sl {indexes}</i> to play the item you want.",
"start_updating": "Start updating...", "start_updating": "Start updating...",
"stopped": "Music stopped.", "stopped": "Music stopped.",
@ -68,7 +68,7 @@
"unable_download": "Unable to download <b>{item}</b>. Removed from the library.", "unable_download": "Unable to download <b>{item}</b>. Removed from the library.",
"unable_play": "Unable to play <b>{item}</b>. Removed from the library.", "unable_play": "Unable to play <b>{item}</b>. Removed from the library.",
"unknown_mode": "Unknown playback mode '{mode}'. It should be one of <i>one-shot</i>, <i>repeat</i>, <i>random</i>.", "unknown_mode": "Unknown playback mode '{mode}'. It should be one of <i>one-shot</i>, <i>repeat</i>, <i>random</i>.",
"update_successful": "<h2>botamusique v{version} Installed!</h2><hr />\n<h3>Changelog</h3> {changelog} <hr /> Visit <a href=\"https://github.com/azlux/botamusique\">our github repo</a> for more details!", "update_successful": "<h2>bragi v{version} Installed!</h2><hr />\n<h3>Changelog</h3> {changelog} <hr /> Visit <a href=\"https://github.com/azlux/bragi\">our github repo</a> for more details!",
"url": "URL", "url": "URL",
"url_ban": "The URL {url} is banned! Removed from playlist!", "url_ban": "The URL {url} is banned! Removed from playlist!",
"url_ban_list": "List of banned URL: <br>{list}", "url_ban_list": "List of banned URL: <br>{list}",
@ -105,7 +105,7 @@
"add_url": "Add URL", "add_url": "Add URL",
"add_youtube_or_soundcloud_url": "Add Youtube or Soundcloud URL", "add_youtube_or_soundcloud_url": "Add Youtube or Soundcloud URL",
"are_you_really_sure": "Are you really sure?", "are_you_really_sure": "Are you really sure?",
"aria_botamusique_logo": "Botamusique Logo: a fox with two headphones, enjoying the music", "aria_bragi_logo": "Botamusique Logo: a fox with two headphones, enjoying the music",
"aria_default_cover": "A black square with two eighth notes beamed together.", "aria_default_cover": "A black square with two eighth notes beamed together.",
"aria_empty_box": "A drawing of an empty box.", "aria_empty_box": "A drawing of an empty box.",
"aria_remove_this_song": "Remove this song from the current playlist", "aria_remove_this_song": "Remove this song from the current playlist",
@ -141,7 +141,7 @@
"no_tag": "No tag", "no_tag": "No tag",
"oneshot": "One-shot", "oneshot": "One-shot",
"open_volume_controls": "Open Volume Controls", "open_volume_controls": "Open Volume Controls",
"page_title": "botamusique Web Interface", "page_title": "bragi Web Interface",
"pause": "Pause", "pause": "Pause",
"play": "Play", "play": "Play",
"playlist_controls": "Playlist controls", "playlist_controls": "Playlist controls",

View File

@ -0,0 +1,4 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import logging import logging
import os import os

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import os import os
import re import re
from io import BytesIO from io import BytesIO

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import logging import logging
item_builders = {} item_builders = {}

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import json import json
import threading import threading
import logging import logging

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import re import re
import logging import logging
import struct import struct

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import threading import threading
import logging import logging
import os import os

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import logging import logging
import yt_dlp as youtube_dl import yt_dlp as youtube_dl
from constants import tr_cli as tr from constants import tr_cli as tr

View File

@ -1,5 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import re import re
import threading import threading
import time import time
@ -36,7 +40,7 @@ class MumbleBot:
def __init__(self, args): def __init__(self, args):
self.log = logging.getLogger("bot") self.log = logging.getLogger("bot")
self.log.info(f"bot: botamusique version {self.get_version()}, starting...") self.log.info(f"bot: bragi version {self.get_version()}, starting...")
signal.signal(signal.SIGINT, self.ctrl_caught) signal.signal(signal.SIGINT, self.ctrl_caught)
self.cmd_handle = {} self.cmd_handle = {}
@ -48,8 +52,6 @@ class MumbleBot:
self.channel = var.config.get("server", "channel") self.channel = var.config.get("server", "channel")
var.user = args.user var.user = args.user
var.is_proxified = var.config.getboolean(
"webinterface", "is_web_proxified")
# Flags to indicate the bot is exiting (Ctrl-C, or !kill) # Flags to indicate the bot is exiting (Ctrl-C, or !kill)
self.exit = False self.exit = False
@ -187,26 +189,7 @@ class MumbleBot:
self.redirect_ffmpeg_log = var.config.getboolean('debug', 'redirect_ffmpeg_log') self.redirect_ffmpeg_log = var.config.getboolean('debug', 'redirect_ffmpeg_log')
if var.config.getboolean("bot", "auto_check_update"): var.db.set("bot", "version", self.version)
def check_update():
nonlocal self
new_version, changelog = util.check_update(self.get_version())
if new_version:
self.send_channel_msg(tr('new_version_found', new_version=new_version, changelog=changelog))
th = threading.Thread(target=check_update, name="UpdateThread")
th.daemon = True
th.start()
last_startup_version = var.db.get("bot", "version", fallback=None)
try:
if not last_startup_version or version.parse(last_startup_version) < version.parse(self.version):
var.db.set("bot", "version", self.version)
if var.config.getboolean("bot", "auto_check_update"):
changelog = util.fetch_changelog()
self.send_channel_msg(tr("update_successful", version=self.version, changelog=changelog))
except version.InvalidVersion:
var.db.set("bot", "version", self.version)
# Set the CTRL+C shortcut # Set the CTRL+C shortcut
def ctrl_caught(self, signal, frame): def ctrl_caught(self, signal, frame):
@ -753,31 +736,13 @@ class MumbleBot:
self.pause_at_id = "" self.pause_at_id = ""
def start_web_interface(addr, port):
global formatter
import interface
# setup logger
werkzeug_logger = logging.getLogger('werkzeug')
logfile = util.solve_filepath(var.config.get('webinterface', 'web_logfile'))
if logfile:
handler = logging.handlers.RotatingFileHandler(logfile, mode='a', maxBytes=10240, backupCount=3) # Rotate after 10KB, leave 3 old logs
else:
handler = logging.StreamHandler()
werkzeug_logger.addHandler(handler)
interface.init_proxy()
interface.web.env = 'development'
interface.web.secret_key = var.config.get('webinterface', 'flask_secret')
interface.web.run(port=port, host=addr)
if __name__ == '__main__': if __name__ == '__main__':
supported_languages = util.get_supported_language() supported_languages = util.get_supported_language()
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Bot for playing music on Mumble') description='Bragi - Bot for playing music on Mumble')
# General arguments # General arguments
parser.add_argument("--config", dest='config', type=str, default='configuration.ini', parser.add_argument("--config", dest='config', type=str, default='configuration.ini',
@ -950,17 +915,6 @@ if __name__ == '__main__':
var.bot_logger.info("bot: load playlist from previous session") var.bot_logger.info("bot: load playlist from previous session")
var.playlist.load() var.playlist.load()
# ============================
# Start the web interface
# ============================
if var.config.getboolean("webinterface", "enabled"):
wi_addr = var.config.get("webinterface", "listening_addr")
wi_port = var.config.getint("webinterface", "listening_port")
tt = threading.Thread(
target=start_web_interface, name="WebThread", args=(wi_addr, wi_port))
tt.daemon = True
bot_logger.info('Starting web interface on {}:{}'.format(wi_addr, wi_port))
tt.start()
# Start the main loop. # Start the main loop.
var.bot.loop() var.bot.loop()

View File

@ -1,4 +1,3 @@
flask
yt-dlp yt-dlp
python-magic python-magic
Pillow Pillow

View File

@ -2,18 +2,18 @@
case "$1" in case "$1" in
stable) stable)
curl -Lo /tmp/botamusique.tar.gz https://packages.azlux.fr/botamusique/sources-stable.tar.gz curl -Lo /tmp/bragi.tar.gz https://packages.azlux.fr/bragi/sources-stable.tar.gz
tar -xzf /tmp/botamusique.tar.gz -C /tmp/ tar -xzf /tmp/bragi.tar.gz -C /tmp/
cp -r /tmp/botamusique/* . cp -r /tmp/bragi/* .
rm -r /tmp/botamusique rm -r /tmp/bragi
rm -r /tmp/botamusique.tar.gz rm -r /tmp/bragi.tar.gz
;; ;;
testing) testing)
curl -Lo /tmp/botamusique.tar.gz https://packages.azlux.fr/botamusique/sources-testing.tar.gz curl -Lo /tmp/bragi.tar.gz https://packages.azlux.fr/bragi/sources-testing.tar.gz
tar -xzf /tmp/botamusique.tar.gz -C /tmp/ tar -xzf /tmp/bragi.tar.gz -C /tmp/
cp -r /tmp/botamusique/* . cp -r /tmp/bragi/* .
rm -r /tmp/botamusique rm -r /tmp/bragi
rm -r /tmp/botamusique.tar.gz rm -r /tmp/bragi.tar.gz
;; ;;
*) *)
;; ;;

68
util.py
View File

@ -1,5 +1,9 @@
#!/usr/bin/python3 #!/usr/bin/python3
# coding=utf-8 # coding=utf-8
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
import hashlib import hashlib
import html import html
@ -102,64 +106,8 @@ def get_user_ban():
return res return res
def new_release_version(target):
if target == "testing":
r = requests.get("https://packages.azlux.fr/botamusique/testing-version")
else:
r = requests.get("https://packages.azlux.fr/botamusique/version")
v = r.text
return v.rstrip()
def fetch_changelog():
r = requests.get("https://packages.azlux.fr/botamusique/changelog")
c = r.text
return c
def check_update(current_version):
global log
log.debug("update: checking for updates...")
new_version = new_release_version(var.config.get('bot', 'target_version'))
if version.parse(new_version) > version.parse(current_version):
changelog = fetch_changelog()
log.info(f"update: new version {new_version} found, current installed version {current_version}.")
log.info(f"update: changelog: {changelog}")
changelog = changelog.replace("\n", "<br>")
return new_version, changelog
else:
log.debug("update: no new version found.")
return None, None
def update(current_version):
global log
target = var.config.get('bot', 'target_version')
new_version = new_release_version(target)
msg = ""
if target == "git":
msg = "git install, I do nothing<br/>"
elif (target == "stable" and version.parse(new_version) > version.parse(current_version)) or \
(target == "testing" and version.parse(new_version) != version.parse(current_version)):
log.info('update: new version, start updating...')
tp = sp.check_output(['/usr/bin/env', 'bash', 'update.sh', target]).decode()
log.debug(tp)
log.info('update: update pip libraries dependencies')
sp.check_output([var.config.get('bot', 'pip3_path'), 'install', '--upgrade', '-r', 'requirements.txt']).decode()
msg = "New version installed, please restart the bot.<br/>"
log.info(f'update: starting update {YT_PKG_NAME} via pip3')
tp = sp.check_output([var.config.get('bot', 'pip3_path'), 'install', '--upgrade', YT_PKG_NAME]).decode()
if f"Collecting {YT_PKG_NAME}" in tp.splitlines():
msg += "Update done: " + tp.split('Successfully installed')[1]
else:
msg += YT_PKG_NAME.capitalize() + " is up-to-date"
reload(youtube_dl)
msg += "<br/>" + YT_PKG_NAME.capitalize() + " reloaded"
return msg
def pipe_no_wait(): def pipe_no_wait():
@ -311,7 +259,7 @@ def get_url_from_input(string):
else: else:
return "" return ""
match = re.search("(http|https)://(\S*)?/(\S*)", string, flags=re.IGNORECASE) match = re.search(r"(http|https)://(\S*)?/(\S*)", string, flags=re.IGNORECASE)
if match: if match:
url = match[1].lower() + "://" + match[2].lower() + "/" + match[3] url = match[1].lower() + "://" + match[2].lower() + "/" + match[3]
# https://github.com/mumble-voip/mumble/issues/4999 # https://github.com/mumble-voip/mumble/issues/4999
@ -376,7 +324,7 @@ def get_media_duration(path):
def parse_time(human): def parse_time(human):
match = re.search("(?:(\d\d):)?(?:(\d\d):)?(\d+(?:\.\d*)?)", human, flags=re.IGNORECASE) match = re.search(r"(?:(\d\d):)?(?:(\d\d):)?(\d+(?:\.\d*)?)", human, flags=re.IGNORECASE)
if match: if match:
if match[1] is None and match[2] is None: if match[1] is None and match[2] is None:
return float(match[3]) return float(match[3])
@ -399,7 +347,7 @@ def format_time(seconds):
def parse_file_size(human): def parse_file_size(human):
units = {"B": 1, "KB": 1024, "MB": 1024 * 1024, "GB": 1024 * 1024 * 1024, "TB": 1024 * 1024 * 1024 * 1024, units = {"B": 1, "KB": 1024, "MB": 1024 * 1024, "GB": 1024 * 1024 * 1024, "TB": 1024 * 1024 * 1024 * 1024,
"K": 1024, "M": 1024 * 1024, "G": 1024 * 1024 * 1024, "T": 1024 * 1024 * 1024 * 1024} "K": 1024, "M": 1024 * 1024, "G": 1024 * 1024 * 1024, "T": 1024 * 1024 * 1024 * 1024}
match = re.search("(\d+(?:\.\d*)?)\s*([A-Za-z]+)", human, flags=re.IGNORECASE) match = re.search(r"(\d+(?:\.\d*)?)\s*([A-Za-z]+)", human, flags=re.IGNORECASE)
if match: if match:
num = float(match[1]) num = float(match[1])
unit = match[2].upper() unit = match[2].upper()
@ -428,7 +376,7 @@ def get_supported_language():
lang_files = os.listdir(os.path.join(root_dir, 'lang')) lang_files = os.listdir(os.path.join(root_dir, 'lang'))
lang_list = [] lang_list = []
for lang_file in lang_files: for lang_file in lang_files:
match = re.search("([a-z]{2}_[A-Z]{2})\.json", lang_file) match = re.search(r"([a-z]{2}_[A-Z]{2})\.json", lang_file)
if match: if match:
lang_list.append(match[1]) lang_list.append(match[1])

View File

@ -1,3 +1,8 @@
#
# Bragi - A Mumble music bot
# Forked from botamusique by azlux (https://github.com/azlux/botamusque)
#
from typing import Type, TYPE_CHECKING from typing import Type, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING: