Fixes + Use SQLAlchemy
This commit is contained in:
parent
023ee141eb
commit
b0ea7ab935
10
code/bot.py
10
code/bot.py
@ -1,10 +1,9 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from discord.ext import tasks
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from utils.database import Base, engine
|
||||||
import utils.config as config
|
import utils.config as config
|
||||||
from utils.jellyfin_delete import delete_accounts
|
|
||||||
|
|
||||||
|
|
||||||
class MyBot(commands.Bot):
|
class MyBot(commands.Bot):
|
||||||
@ -15,7 +14,6 @@ class MyBot(commands.Bot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def setup_hook(self):
|
async def setup_hook(self):
|
||||||
delete_old_temp_accounts.start()
|
|
||||||
for ext in os.listdir("./code/cogs"):
|
for ext in os.listdir("./code/cogs"):
|
||||||
if ext.endswith(".py"):
|
if ext.endswith(".py"):
|
||||||
await self.load_extension(f"cogs.{ext[:-3]}")
|
await self.load_extension(f"cogs.{ext[:-3]}")
|
||||||
@ -30,11 +28,7 @@ async def on_ready():
|
|||||||
config.LOG.info(f"{bot.user} has connected to Discord.")
|
config.LOG.info(f"{bot.user} has connected to Discord.")
|
||||||
|
|
||||||
|
|
||||||
@tasks.loop(seconds=60)
|
|
||||||
async def delete_old_temp_accounts():
|
|
||||||
delete_accounts()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
config.load_config()
|
config.load_config()
|
||||||
bot.run(config.BOT_TOKEN)
|
bot.run(config.BOT_TOKEN)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands, tasks
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
|
from utils.database import Session
|
||||||
from utils.jellyfin_create import create_jellyfin_account
|
from utils.jellyfin_create import create_jellyfin_account
|
||||||
|
from utils.jellyfin_delete import delete_accounts
|
||||||
|
from utils.models import JellyfinAccounts
|
||||||
from utils.config import (
|
from utils.config import (
|
||||||
JELLYFIN_PUBLIC_URL,
|
JELLYFIN_PUBLIC_URL,
|
||||||
JELLYFIN_ENABLED,
|
JELLYFIN_ENABLED,
|
||||||
@ -15,19 +17,20 @@ class NewAccount(commands.Cog):
|
|||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
|
def cog_load(self):
|
||||||
|
self.delete_accounts_loop.start()
|
||||||
|
|
||||||
@app_commands.command()
|
@app_commands.command()
|
||||||
@app_commands.check(lambda inter: JELLYFIN_ENABLED)
|
@app_commands.check(lambda inter: JELLYFIN_ENABLED)
|
||||||
async def newaccount(self, interaction: discord.Interaction) -> None:
|
async def newaccount(self, interaction: discord.Interaction) -> None:
|
||||||
"""Create a new temporary Jellyfin account"""
|
"""Create a new temporary Jellyfin account"""
|
||||||
# Make sure the user doesn't already have an account
|
# Make sure the user doesn't already have an account
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
with Session() as session:
|
||||||
cursor = db.cursor()
|
account = (
|
||||||
cursor.execute(
|
session.query(JellyfinAccounts)
|
||||||
"SELECT * FROM jellyfin_accounts WHERE user_id = ?",
|
.filter(JellyfinAccounts.user_id == interaction.user.id)
|
||||||
(interaction.user.id,),
|
.first()
|
||||||
)
|
)
|
||||||
account = cursor.fetchone()
|
|
||||||
db.close()
|
|
||||||
# Account already allocated
|
# Account already allocated
|
||||||
if account:
|
if account:
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
@ -63,8 +66,8 @@ class NewAccount(commands.Cog):
|
|||||||
title="Jellyfin Account Information",
|
title="Jellyfin Account Information",
|
||||||
description=(
|
description=(
|
||||||
# fmt: off
|
# fmt: off
|
||||||
"Here is your temporary account information.\n\n",
|
"Here is your temporary account information.\n\n"
|
||||||
f"**Server URL:** `[{JELLYFIN_PUBLIC_URL}]({JELLYFIN_PUBLIC_URL})`\n"
|
f"**Server URL:** `{JELLYFIN_PUBLIC_URL}`\n"
|
||||||
f"**Username:** `{response[0]}`\n"
|
f"**Username:** `{response[0]}`\n"
|
||||||
f"**Password:** `{response[1]}`\n\n"
|
f"**Password:** `{response[1]}`\n\n"
|
||||||
"Your account will be automatically deleted in"
|
"Your account will be automatically deleted in"
|
||||||
@ -88,6 +91,10 @@ class NewAccount(commands.Cog):
|
|||||||
embed=embed, ephemeral=True
|
embed=embed, ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@tasks.loop(minutes=1)
|
||||||
|
async def delete_accounts_loop(self):
|
||||||
|
delete_accounts()
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
async def setup(bot):
|
||||||
await bot.add_cog(NewAccount(bot))
|
await bot.add_cog(NewAccount(bot))
|
||||||
|
@ -2,8 +2,9 @@ import discord
|
|||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import requests
|
import requests
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
|
from utils.models import Requests
|
||||||
|
from utils.database import Session
|
||||||
from utils.config import (
|
from utils.config import (
|
||||||
RADARR_HOST_URL,
|
RADARR_HOST_URL,
|
||||||
RADARR_HEADERS,
|
RADARR_HEADERS,
|
||||||
@ -22,15 +23,18 @@ class Status(commands.Cog):
|
|||||||
# Defer the response
|
# Defer the response
|
||||||
await interaction.response.defer(ephemeral=True)
|
await interaction.response.defer(ephemeral=True)
|
||||||
|
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
with Session() as session:
|
||||||
cursor = db.cursor()
|
requested_content = (
|
||||||
cursor.execute(
|
session.query(
|
||||||
"SELECT title, release_year, local_id, tmdbid, tvdbid FROM"
|
Requests.title,
|
||||||
" requests WHERE user_id = ?",
|
Requests.release_year,
|
||||||
(interaction.user.id,),
|
Requests.local_id,
|
||||||
)
|
Requests.tmdbid,
|
||||||
requested_content = cursor.fetchall()
|
Requests.tvdbid,
|
||||||
db.close()
|
)
|
||||||
|
.filter(Requests.user_id == interaction.user.id)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# No content requested
|
# No content requested
|
||||||
if len(requested_content) == 0:
|
if len(requested_content) == 0:
|
||||||
@ -42,7 +46,7 @@ class Status(commands.Cog):
|
|||||||
),
|
),
|
||||||
color=0xD01B86,
|
color=0xD01B86,
|
||||||
)
|
)
|
||||||
return await interaction.followup.send(embed=embed, ephemeral=True)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
# Create template embed
|
# Create template embed
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
@ -75,7 +79,7 @@ class Status(commands.Cog):
|
|||||||
embed.description += radarr_desc + sonarr_desc + non_queue_desc
|
embed.description += radarr_desc + sonarr_desc + non_queue_desc
|
||||||
|
|
||||||
# Send the follow-up message
|
# Send the follow-up message
|
||||||
await interaction.edit_original_response(embed=embed, ephemeral=True)
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
def unpack_content(self, requested_content: list) -> tuple:
|
def unpack_content(self, requested_content: list) -> tuple:
|
||||||
"""
|
"""
|
||||||
@ -92,18 +96,18 @@ class Status(commands.Cog):
|
|||||||
sonarr_content_info = {}
|
sonarr_content_info = {}
|
||||||
|
|
||||||
for content in requested_content:
|
for content in requested_content:
|
||||||
title, (release_year), local_id, tmdbid, tvdbid = content
|
title, release_year, local_id, tmdbid, tvdbid = content
|
||||||
if tmdbid is not None:
|
if tmdbid is not None:
|
||||||
radarr_content_info[int(local_id)] = {
|
radarr_content_info[local_id] = {
|
||||||
"title": title,
|
"title": title,
|
||||||
"release_year": int(release_year),
|
"release_year": release_year,
|
||||||
"tmdbid": int(tmdbid),
|
"tmdbid": tmdbid,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
sonarr_content_info[int(local_id)] = {
|
sonarr_content_info[local_id] = {
|
||||||
"title": title,
|
"title": title,
|
||||||
"release_year": int(release_year),
|
"release_year": release_year,
|
||||||
"tvdbid": int(tvdbid),
|
"tvdbid": tvdbid,
|
||||||
}
|
}
|
||||||
|
|
||||||
return radarr_content_info, sonarr_content_info
|
return radarr_content_info, sonarr_content_info
|
||||||
@ -169,7 +173,7 @@ class Status(commands.Cog):
|
|||||||
for content in requested_content:
|
for content in requested_content:
|
||||||
title, release_year, local_id, tmdbid, _ = content
|
title, release_year, local_id, tmdbid, _ = content
|
||||||
# If not in queue
|
# If not in queue
|
||||||
if int(local_id) not in added_ids:
|
if local_id not in added_ids:
|
||||||
# Pull the movie data from the service
|
# Pull the movie data from the service
|
||||||
if tmdbid is not None:
|
if tmdbid is not None:
|
||||||
data = requests.get(
|
data = requests.get(
|
||||||
@ -185,15 +189,16 @@ class Status(commands.Cog):
|
|||||||
# If the movie has a file, then it has finished downloading
|
# If the movie has a file, then it has finished downloading
|
||||||
if data.get("hasFile", True):
|
if data.get("hasFile", True):
|
||||||
# Remove from database
|
# Remove from database
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
with Session() as session:
|
||||||
cursor = db.cursor()
|
request = (
|
||||||
cursor.execute(
|
session.query(Requests)
|
||||||
"DELETE FROM requests WHERE user_id = ? AND"
|
.filter(Requests.user_id == user_id)
|
||||||
" local_id = ?",
|
.filter(Requests.local_id == local_id)
|
||||||
(user_id, int(local_id)),
|
.first()
|
||||||
)
|
)
|
||||||
db.commit()
|
session.delete(request)
|
||||||
db.close()
|
session.commit()
|
||||||
|
|
||||||
# If series and only a portion of episodes have been downloaded
|
# If series and only a portion of episodes have been downloaded
|
||||||
if data.get("statistics").get("percentOfEpisodes"):
|
if data.get("statistics").get("percentOfEpisodes"):
|
||||||
description += (
|
description += (
|
||||||
|
@ -4,7 +4,6 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import sqlite3
|
|
||||||
from colorlog import ColoredFormatter
|
from colorlog import ColoredFormatter
|
||||||
|
|
||||||
|
|
||||||
@ -108,10 +107,9 @@ schema = {
|
|||||||
|
|
||||||
def load_config() -> None:
|
def load_config() -> None:
|
||||||
"""
|
"""
|
||||||
Load DB, then load and validate the config file
|
Load the config file and validate it
|
||||||
If the file does not exist, generate it
|
If the file does not exist, generate it
|
||||||
"""
|
"""
|
||||||
database_setup()
|
|
||||||
if os.path.exists("/.dockerenv"):
|
if os.path.exists("/.dockerenv"):
|
||||||
file_path = "config/config.yaml"
|
file_path = "config/config.yaml"
|
||||||
else:
|
else:
|
||||||
@ -157,26 +155,6 @@ jellyfin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def database_setup() -> None:
|
|
||||||
"""
|
|
||||||
Create the database if it does not exist
|
|
||||||
"""
|
|
||||||
if not os.path.exists("data"):
|
|
||||||
os.makedirs("data")
|
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
|
||||||
cursor = db.cursor()
|
|
||||||
cursor.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS requests (title TEXT, release_year TEXT,"
|
|
||||||
" local_id INTEGER, tmdbid INTEGER, tvdbid INTEGER, user_id INTEGER)"
|
|
||||||
)
|
|
||||||
cursor.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS jellyfin_accounts (user_id INTEGER,"
|
|
||||||
" jellyfin_user_id INTEGER, deletion_time DATETIME)"
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
def validate_config(contents) -> None:
|
def validate_config(contents) -> None:
|
||||||
"""
|
"""
|
||||||
Validate the contents of the config file and assign variables
|
Validate the contents of the config file and assign variables
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import discord
|
import discord
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
|
from utils.models import Requests
|
||||||
|
from utils.database import Session
|
||||||
from utils.content_add import add_content
|
from utils.content_add import add_content
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -163,30 +164,26 @@ class RequestButtonView(discord.ui.View):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Keep track of the requests for the `/status` command
|
# Keep track of the requests for the `/status` command
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
with Session() as session:
|
||||||
cursor = db.cursor()
|
session.add(
|
||||||
cursor.execute(
|
Requests(
|
||||||
"INSERT INTO requests (title, release_year, local_id, tmdbid,"
|
title=self.content_info["title"],
|
||||||
" tvdbid, user_id) VALUES (?, ?, ?, ?, ?, ?)",
|
release_year=self.content_info["year"],
|
||||||
(
|
local_id=local_id,
|
||||||
self.content_info["title"],
|
tmdbid=(
|
||||||
self.content_info["year"],
|
self.content_info["contentId"]
|
||||||
local_id,
|
if self.service == "radarr"
|
||||||
(
|
else None
|
||||||
self.content_info["contentId"]
|
),
|
||||||
if self.service == "radarr"
|
tvdbid=(
|
||||||
else None
|
None
|
||||||
),
|
if self.service == "radarr"
|
||||||
(
|
else self.content_info["contentId"]
|
||||||
None
|
),
|
||||||
if self.service == "radarr"
|
user_id=interaction.user.id,
|
||||||
else self.content_info["contentId"]
|
)
|
||||||
),
|
)
|
||||||
interaction.user.id,
|
session.commit()
|
||||||
),
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
@discord.ui.button(label="Don't Request", style=discord.ButtonStyle.danger)
|
@discord.ui.button(label="Don't Request", style=discord.ButtonStyle.danger)
|
||||||
async def dont_request_button(
|
async def dont_request_button(
|
||||||
|
9
code/utils/database.py
Normal file
9
code/utils/database.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
database_url = "sqlite:///data/cordarr.db"
|
||||||
|
|
||||||
|
engine = create_engine(database_url)
|
||||||
|
Session = sessionmaker(bind=engine)
|
||||||
|
Base = declarative_base()
|
@ -1,10 +1,11 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import requests
|
import requests
|
||||||
import random
|
import random
|
||||||
import sqlite3
|
|
||||||
from wonderwords import RandomWord
|
from wonderwords import RandomWord
|
||||||
from string import ascii_lowercase, digits
|
from string import ascii_lowercase, digits
|
||||||
|
|
||||||
|
from utils.database import Session
|
||||||
|
from utils.models import JellyfinAccounts
|
||||||
from utils.config import (
|
from utils.config import (
|
||||||
JELLYFIN_URL,
|
JELLYFIN_URL,
|
||||||
JELLYFIN_HEADERS,
|
JELLYFIN_HEADERS,
|
||||||
@ -67,14 +68,14 @@ def create_jellyfin_account(user_id):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Add the information to the database
|
# Add the information to the database
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
with Session() as session:
|
||||||
cursor = db.cursor()
|
session.add(
|
||||||
cursor.execute(
|
JellyfinAccounts(
|
||||||
"INSERT INTO jellyfin_accounts (user_id, jellyfin_user_id,"
|
user_id=user_id,
|
||||||
" deletion_time) VALUES (?, ?, ?)",
|
jellyfin_user_id=jellyfin_user_id,
|
||||||
(user_id, jellyfin_user_id, deletion_time),
|
deletion_time=deletion_time,
|
||||||
)
|
)
|
||||||
db.commit()
|
)
|
||||||
db.close()
|
session.commit()
|
||||||
|
|
||||||
return username, password
|
return username, password
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import sqlite3
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from utils.config import JELLYFIN_URL, JELLYFIN_HEADERS
|
from utils.database import Session
|
||||||
|
from utils.models import JellyfinAccounts
|
||||||
|
from utils.config import LOG, JELLYFIN_URL, JELLYFIN_HEADERS
|
||||||
|
|
||||||
|
|
||||||
def delete_accounts():
|
def delete_accounts():
|
||||||
@ -10,29 +11,36 @@ def delete_accounts():
|
|||||||
Delete Jellyfin accounts that have passed their deletion time
|
Delete Jellyfin accounts that have passed their deletion time
|
||||||
"""
|
"""
|
||||||
# Get all expired Jellyfin accounts
|
# Get all expired Jellyfin accounts
|
||||||
db = sqlite3.connect("data/cordarr.db")
|
with Session() as session:
|
||||||
cursor = db.cursor()
|
jellyfin_user_ids = (
|
||||||
cursor.execute(
|
session.query(JellyfinAccounts.jellyfin_user_id)
|
||||||
"SELECT jellyfin_user_id FROM jellyfin_accounts WHERE"
|
.filter(JellyfinAccounts.deletion_time < datetime.datetime.now())
|
||||||
" deletion_time < ?",
|
.all()
|
||||||
(datetime.datetime.now(),),
|
|
||||||
)
|
|
||||||
jellyfin_user_ids = cursor.fetchall()
|
|
||||||
|
|
||||||
# Delete the Jellyfin accounts
|
|
||||||
for jellyfin_user_id in jellyfin_user_ids:
|
|
||||||
request = requests.delete(
|
|
||||||
f"{JELLYFIN_URL}/Users/{jellyfin_user_id[0]}",
|
|
||||||
headers=JELLYFIN_HEADERS,
|
|
||||||
)
|
)
|
||||||
# If 204 - account deleted
|
|
||||||
# If 404 - account not found
|
|
||||||
# Either way, remove account from database
|
|
||||||
if request.status_code in (404, 204):
|
|
||||||
cursor.execute(
|
|
||||||
"DELETE FROM jellyfin_accounts WHERE jellyfin_user_id = ?",
|
|
||||||
(jellyfin_user_id,),
|
|
||||||
)
|
|
||||||
|
|
||||||
db.commit()
|
# Delete each account
|
||||||
db.close()
|
for jellyfin_user_id in jellyfin_user_ids:
|
||||||
|
print(f"Deleting account {jellyfin_user_id[0]}")
|
||||||
|
try:
|
||||||
|
response = requests.delete(
|
||||||
|
f"{JELLYFIN_URL}/Users/{jellyfin_user_id[0]}",
|
||||||
|
headers=JELLYFIN_HEADERS,
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
# Get the account and delete it
|
||||||
|
account = (
|
||||||
|
session.query(JellyfinAccounts)
|
||||||
|
.filter(
|
||||||
|
JellyfinAccounts.jellyfin_user_id
|
||||||
|
== jellyfin_user_id[0]
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
session.delete(account)
|
||||||
|
except:
|
||||||
|
LOG.error(
|
||||||
|
"Failed deleting Jellyfin account w/ ID"
|
||||||
|
f" {jellyfin_user_id[0]}"
|
||||||
|
)
|
||||||
|
# Commit changes
|
||||||
|
session.commit()
|
||||||
|
31
code/utils/models.py
Normal file
31
code/utils/models.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from sqlalchemy import (
|
||||||
|
Column,
|
||||||
|
Integer,
|
||||||
|
String,
|
||||||
|
DateTime,
|
||||||
|
BigInteger,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
from utils.database import Base
|
||||||
|
|
||||||
|
|
||||||
|
class Requests(Base):
|
||||||
|
__tablename__ = "requests"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
title = Column(String)
|
||||||
|
release_year = Column(Integer)
|
||||||
|
local_id = Column(Integer)
|
||||||
|
tmdbid = Column(Integer)
|
||||||
|
tvdbid = Column(Integer)
|
||||||
|
user_id = Column(BigInteger)
|
||||||
|
|
||||||
|
|
||||||
|
class JellyfinAccounts(Base):
|
||||||
|
__tablename__ = "jellyfin_accounts"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
user_id = Column(BigInteger)
|
||||||
|
jellyfin_user_id = Column(String)
|
||||||
|
deletion_time = Column(DateTime)
|
@ -4,4 +4,5 @@ wonderwords==2.2.0
|
|||||||
PyYAML==6.0.2
|
PyYAML==6.0.2
|
||||||
jsonschema==4.23.0
|
jsonschema==4.23.0
|
||||||
jsonschema-specifications==2024.10.1
|
jsonschema-specifications==2024.10.1
|
||||||
discord.py==2.4.0
|
discord.py==2.4.0
|
||||||
|
SQLAlchemy==2.0.37
|
Loading…
x
Reference in New Issue
Block a user