Compare commits

...

3 commits

8 changed files with 121 additions and 20 deletions

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
.env
.venv
.cache/*
cache/*
.logs/*
logs/*
sounds/originals/*

View file

@ -22,31 +22,52 @@ Copy `.env.example` to `.env`:
- Settings > Advanced > Developer Mode: enabled
- Right click the guild and select "Copy ID"
- `DISCORD_STATUS_WEBHOOK_URL` (optional) is used to monitor when the bot fails
- Channel Settings > Integrations > New Webhook (don't forget to save changes)
- Copy Webhook URL
- `DISCORD_BOT_ADMIN_ROLE_ID` for reloading cogs
- `DISCORD_BOT_ADMIN_USER_ID` for reloading cogs
- `DISCORD_VCJOIN_CHANNEL` for the VCJoin cog
## Setup
### ...with PDM
### with systemd
```sh
pdm install --prod
pdm run python app.py
```
mkdir -p /srv/discord
cd /srv/discord
### ...with requirements.txt and virtualenv
sudo useradd --system -s /srv/discord discord
sudo chown -R discord:discord /srv/discord
```sh
virtualenv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
sudo -Hu discord git clone https://git.trwnh.com/a/umi.git
python -m venv .venv
.venv/bin/pip install -r umi/requirements.txt
sudo cp umi/umi@.service /etc/systemd/system/umi@.service
sudo systemctl daemon-reload
sudo systemctl start umi@umi
```
### ...with Docker Compose
```sh
docker compose build
docker compose up -d
```
mkdir -p /srv/discord
cd /srv/discord
sudo useradd --system -s /srv/discord discord
sudo chown -R discord:discord /srv/discord
sudo -Hu discord git clone https://git.trwnh.com/a/umi.git
cd umi
sudo docker compose build
sudo docker compose up -d
```
## Log into YouTube
- Follow the instructions at https://github.com/yt-dlp/yt-dlp/wiki/Extractors#exporting-youtube-cookies
- Put your extracted cookies into config/cookies.txt

0
cache/.gitkeep vendored Normal file
View file

View file

@ -593,6 +593,65 @@ class Music(Cog):
logger.info(f"Message sent: Playing {len(tracks)} tracks.")
await self.play_next(ctx)
@command()
async def ytlogin(self, ctx: Context):
"""Print token and instructions for authorizing with YouTube"""
logger.info(f".ytlogin")
process = subprocess.Popen(
[
"yt-dlp",
"--netrc",
"--skip-download", # we don't care to actually download the video, we just wanna authorize
"https://www.youtube.com/watch?v=dQw4w9WgXcQ" # this can be anything, it just needs a youtube URL
],
stdout=subprocess.PIPE,
encoding="utf-8",
)
while True:
line = process.stdout.readline()
if not line:
logger.debug("Subprocess ran out of lines in stdout")
break
if "google.com" in line:
msg = await ctx.send(line)
if msg:
logger.info(f"Message sent: {line}")
break
timeout = time() + 30 # TODO: make this variable instead of hardcoded?
while (process.poll() is None) and (time() < timeout):
pass # this loop will break when the process exits with a return code, or when the timeout is exceeded
if process.poll() is None:
logger.debug("Process still hasn't exited yet, so we need to kill it and let the user know that the authorization process failed")
process.kill()
text = "Authorization process timed out."
msg = await ctx.send(text)
if msg:
logger.info(f"Message sent: {text}")
return
else:
logger.debug("Process has exited with a return code, so check if the return code indicates no errors.")
if process.returncode == 0:
text = "The process has finished with no errors, so you're probably logged in now."
msg = await ctx.send(text)
if msg:
logger.info(f"Message sent: {text}")
process.kill() # just in case
return
else:
process.kill() # just in case
out, err = process.communicate()
if err:
logger.error(f"An error occurred during the authorization process: {err}")
msg = await ctx.send(
f"An error occurred during the authorization process:\n",
f"```\n",
f"{err}",
f"```",
)
if msg:
logger.info(f"Message sent: An error occurred during the authorization process")
return
@command(aliases=['p', 'listen'])
async def play(self, ctx: Context, *, query: str = ""):
"""Add track(s) to queue"""
@ -864,11 +923,8 @@ ytdl_format_options = {
"default_search": "auto",
# "source_address": "0.0.0.0", # Bind to ipv4 since ipv6 addresses cause issues
"extract_flat": True, # massive speedup for fetching metadata, at the cost of no upload date
#"cookiefile": "cookies.txt",
"cookiefile": "config/cookies.txt",
"cachedir": "cache",
"username": "oauth",
"password": "",
#"usenetrc": True,
}
username = getenv("YOUTUBE_USERNAME")
password = getenv("YOUTUBE_PASSWORD")
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)

1
config/.netrc Normal file
View file

@ -0,0 +1 @@
machine youtube login oauth password ""

7
contrib/notify.sh Normal file
View file

@ -0,0 +1,7 @@
#!/usr/bin/bash
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"username": "system", "content": "'"$1"'"}' \
$DISCORD_STATUS_WEBHOOK_URL

15
contrib/umi@.service Normal file
View file

@ -0,0 +1,15 @@
[Unit]
Description=Discord bot for logging VC joins and playing music
After=network.target
[Service]
Restart=on-failure
User=discord
WorkingDirectory=/srv/discord/%I
EnvironmentFile=/srv/discord/%I/.env
ExecStart=/srv/discord/.venv/bin/python -u app.py
ExecStopPost=/srv/discord/%I/notify.sh "umi instance stopped: `%i`"
ExecStartPost=/srv/discord/%I/notify.sh "umi instance started: `%i`"
[Install]
WantedBy=multi-user.target

View file

@ -13,6 +13,6 @@ services:
- ./cogs:/umi/cogs
- ./sounds:/umi/sounds
- ./logs:/umi/.logs
- ./cookies.txt:/umi/cookies.txt
- ./.netrc:/root/.netrc
- ./config/cookies.txt:/umi/cookies.txt
- ./config/.netrc:/root/.netrc
- ./cache:/umi/cache