Initial Push
This commit is contained in:
19
.env
Normal file
19
.env
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Discord
|
||||||
|
DISCORD_TOKEN=your_discord_bot_token
|
||||||
|
PREFIX=!
|
||||||
|
GUILD_ID=123456789012345678
|
||||||
|
ADMIN_ROLE_IDS=111111111111111111,222222222222222222
|
||||||
|
REQUIRED_ROLE_IDS=333333333333333333,444444444444444444
|
||||||
|
|
||||||
|
# Jellyfin
|
||||||
|
JELLYFIN_URL=http://127.0.0.1:8096
|
||||||
|
JELLYFIN_API_KEY=your_jellyfin_api_key
|
||||||
|
|
||||||
|
# MySQL
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=password
|
||||||
|
DB_NAME=jellyfin_bot
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
SYNC_LOG_CHANNEL_ID=555555555555555555
|
||||||
51
README.md
51
README.md
@@ -1,2 +1,53 @@
|
|||||||
# Jellyfin-Discord
|
# Jellyfin-Discord
|
||||||
Allow the creation and management of Jellyfin users via Discord
|
Allow the creation and management of Jellyfin users via Discord
|
||||||
|
|
||||||
|
Join my [Discord](https://discord.com/invite/zJMUNCPtPy) for help, and keeping an eye out for updates!
|
||||||
|
|
||||||
|
This is a very simple and lightweight Jellyfin Discord bot for managing users. It allows for creation of accounts, password recovery, account deletion, ect.
|
||||||
|
|
||||||
|
Fill out values in the .env and you're good to go!
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- Automatic Account Cleanup
|
||||||
|
- Creating Accounts
|
||||||
|
- Recovering Passwords
|
||||||
|
- Searching accounts by Discord User, or Jellyfin User
|
||||||
|
- Run Library Scanning
|
||||||
|
- Manual Account Linking (For previously made Jellyfin accounts)
|
||||||
|
- Change bot prefix live
|
||||||
|
|
||||||
|
# Command Overview
|
||||||
|
|
||||||
|
**Pinging the bot will show you the necessary commands to create your account.**
|
||||||
|
|
||||||
|
**PLEASE NOTE BEFORE USING. THIS BOT IS MEANT TO USE REQUIRED ROLES IN ORDER TO WHITELIST USERS FOR JELLYFIN. TAKING A USERS ROLE AWAY WILL DELETE THEIR JELLYFIN ACCOUNT WHEN THE BOT RUNS ITS CLEANUP (24 Hour Schedule or Admin Forced)**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**There are protections in place to stop users from creating an account where people can see. If a user sends the account creation or reset in a guild, the bot will delete it.**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**If a user already has a linked Jellyfin account, the bot will not allow them to create another account.**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**In order to create an account, you must have the required roles specified in the .env**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***User Commands***
|
||||||
|
- !createaccount <username> <password> - Create your Jellyfin account
|
||||||
|
- !recoveraccount <username> <newpassword> - Reset your password
|
||||||
|
- !deleteaccount <username> - Delete your Jellyfin account
|
||||||
|
|
||||||
|
***Admin Commands***
|
||||||
|
- !syncaccounts - Remove Jellyfin accounts from users without roles
|
||||||
|
- !searchaccount <jellyfin_username> - Find linked Discord user
|
||||||
|
- !searchdiscord @user - Find linked Jellyfin account
|
||||||
|
- !scanlibraries - Scan all Jellyfin libraries
|
||||||
|
- !assignaccount <jellyfin_username> @user - Manually link accounts
|
||||||
|
|
||||||
|
***Admin Bot Commands***
|
||||||
|
- !setprefix - Change the bots command prefix
|
||||||
412
app.py
Normal file
412
app.py
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands, tasks
|
||||||
|
import requests
|
||||||
|
import mysql.connector
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# ENV + VALIDATION
|
||||||
|
# =====================
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
def get_env_var(key: str, cast=str, required=True):
|
||||||
|
value = os.getenv(key)
|
||||||
|
if required and (value is None or value.strip() == ""):
|
||||||
|
raise ValueError(f"❌ Missing required environment variable: {key}")
|
||||||
|
try:
|
||||||
|
return cast(value) if value is not None else None
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(f"❌ Invalid value for {key}, expected {cast.__name__}")
|
||||||
|
|
||||||
|
TOKEN = get_env_var("DISCORD_TOKEN")
|
||||||
|
PREFIX = os.getenv("PREFIX", "!") # Default to "!" if not set
|
||||||
|
GUILD_ID = get_env_var("GUILD_ID", int)
|
||||||
|
REQUIRED_ROLE_IDS = [int(x) for x in get_env_var("REQUIRED_ROLE_IDS").split(",")]
|
||||||
|
ADMIN_ROLE_IDS = [int(x) for x in get_env_var("ADMIN_ROLE_IDS").split(",")]
|
||||||
|
SYNC_LOG_CHANNEL_ID = get_env_var("SYNC_LOG_CHANNEL_ID", int)
|
||||||
|
|
||||||
|
JELLYFIN_URL = get_env_var("JELLYFIN_URL")
|
||||||
|
JELLYFIN_API_KEY = get_env_var("JELLYFIN_API_KEY")
|
||||||
|
|
||||||
|
DB_HOST = get_env_var("DB_HOST")
|
||||||
|
DB_USER = get_env_var("DB_USER")
|
||||||
|
DB_PASSWORD = get_env_var("DB_PASSWORD")
|
||||||
|
DB_NAME = get_env_var("DB_NAME")
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# DISCORD SETUP
|
||||||
|
# =====================
|
||||||
|
intents = discord.Intents.all()
|
||||||
|
intents.members = True
|
||||||
|
intents.message_content = True
|
||||||
|
bot = commands.Bot(command_prefix=PREFIX, intents=intents, help_command=None)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# DATABASE SETUP
|
||||||
|
# =====================
|
||||||
|
def init_db():
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute(f"CREATE DATABASE IF NOT EXISTS `{DB_NAME}`")
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD, database=DB_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS accounts (
|
||||||
|
discord_id BIGINT PRIMARY KEY,
|
||||||
|
jellyfin_username VARCHAR(255) NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def add_account(discord_id, jellyfin_username):
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD, database=DB_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("REPLACE INTO accounts (discord_id, jellyfin_username) VALUES (%s, %s)",
|
||||||
|
(discord_id, jellyfin_username))
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def get_accounts():
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD, database=DB_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("SELECT discord_id, jellyfin_username FROM accounts")
|
||||||
|
rows = cur.fetchall()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def get_account_by_jellyfin(username):
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD, database=DB_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("SELECT discord_id FROM accounts WHERE jellyfin_username=%s", (username,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return row
|
||||||
|
|
||||||
|
def get_account_by_discord(discord_id):
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD, database=DB_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("SELECT jellyfin_username FROM accounts WHERE discord_id=%s", (discord_id,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
return row
|
||||||
|
|
||||||
|
def delete_account(discord_id):
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=DB_HOST, user=DB_USER, password=DB_PASSWORD, database=DB_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("DELETE FROM accounts WHERE discord_id=%s", (discord_id,))
|
||||||
|
conn.commit()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# JELLYFIN HELPERS
|
||||||
|
# =====================
|
||||||
|
def create_jellyfin_user(username, password):
|
||||||
|
headers = {"X-Emby-Token": JELLYFIN_API_KEY}
|
||||||
|
data = {"Name": username, "Password": password}
|
||||||
|
r = requests.post(f"{JELLYFIN_URL}/Users/New", json=data, headers=headers)
|
||||||
|
return r.status_code == 200
|
||||||
|
|
||||||
|
def get_jellyfin_user(username):
|
||||||
|
headers = {"X-Emby-Token": JELLYFIN_API_KEY}
|
||||||
|
r = requests.get(f"{JELLYFIN_URL}/Users", headers=headers)
|
||||||
|
if r.status_code == 200:
|
||||||
|
for u in r.json():
|
||||||
|
if u["Name"].lower() == username.lower():
|
||||||
|
return u["Id"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def delete_jellyfin_user(username):
|
||||||
|
headers = {"X-Emby-Token": JELLYFIN_API_KEY}
|
||||||
|
user_id = get_jellyfin_user(username)
|
||||||
|
if user_id:
|
||||||
|
r = requests.delete(f"{JELLYFIN_URL}/Users/{user_id}", headers=headers)
|
||||||
|
return r.status_code in (200, 204)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def reset_jellyfin_password(username: str, new_password: str) -> bool:
|
||||||
|
user_id = get_jellyfin_user(username)
|
||||||
|
if not user_id:
|
||||||
|
return False
|
||||||
|
headers = {"X-Emby-Token": JELLYFIN_API_KEY}
|
||||||
|
data = {"Password": new_password}
|
||||||
|
response = requests.post(f"{JELLYFIN_URL}/Users/{user_id}/Password", headers=headers, json=data)
|
||||||
|
return response.status_code in (200, 204)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# DISCORD HELPERS
|
||||||
|
# =====================
|
||||||
|
def has_required_role(member):
|
||||||
|
return any(role.id in REQUIRED_ROLE_IDS for role in member.roles)
|
||||||
|
|
||||||
|
def has_admin_role(member):
|
||||||
|
return any(role.id in ADMIN_ROLE_IDS for role in member.roles)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# EVENTS
|
||||||
|
# =====================
|
||||||
|
@bot.event
|
||||||
|
async def on_message(message):
|
||||||
|
if message.author == bot.user:
|
||||||
|
return
|
||||||
|
|
||||||
|
if bot.user in message.mentions:
|
||||||
|
instructions = (
|
||||||
|
f"👋 Hi {message.author.mention}!\n\n"
|
||||||
|
"To create a Jellyfin account, please DM me the following command:\n"
|
||||||
|
f"`{PREFIX}createaccount <username> <password>`\n\n"
|
||||||
|
"To reset your password, DM me:\n"
|
||||||
|
f"`{PREFIX}recoveraccount <username> <newpassword>`\n\n"
|
||||||
|
f"Make sure you have the required server role(s) to create an account."
|
||||||
|
)
|
||||||
|
await message.channel.send(instructions)
|
||||||
|
|
||||||
|
await bot.process_commands(message)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# COMMANDS
|
||||||
|
# =====================
|
||||||
|
@bot.command()
|
||||||
|
async def createaccount(ctx, username: str, password: str):
|
||||||
|
if not isinstance(ctx.channel, discord.DMChannel):
|
||||||
|
await ctx.message.delete()
|
||||||
|
await ctx.send(f"{ctx.author.mention} Please DM me to create your Jellyfin account.")
|
||||||
|
return
|
||||||
|
|
||||||
|
guild = bot.get_guild(GUILD_ID)
|
||||||
|
member = guild.get_member(ctx.author.id)
|
||||||
|
|
||||||
|
if not member or not has_required_role(member):
|
||||||
|
await ctx.send("❌ You don’t have the required role to create an account.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if get_account_by_discord(ctx.author.id):
|
||||||
|
await ctx.send("❌ You already have a Jellyfin account.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if create_jellyfin_user(username, password):
|
||||||
|
add_account(ctx.author.id, username)
|
||||||
|
await ctx.send(f"✅ Account created! You can log in at {JELLYFIN_URL}")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ Failed to create account. Username may already exist.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def recoveraccount(ctx, new_password: str):
|
||||||
|
"""DM-only: reset your Jellyfin password"""
|
||||||
|
# Ensure it's a DM
|
||||||
|
if not isinstance(ctx.channel, discord.DMChannel):
|
||||||
|
await ctx.message.delete()
|
||||||
|
await ctx.send(f"{ctx.author.mention} Please DM me to reset your password.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Fetch the Jellyfin account linked to this Discord user
|
||||||
|
acc = get_account_by_discord(ctx.author.id)
|
||||||
|
if not acc:
|
||||||
|
await ctx.send("❌ You do not have a linked Jellyfin account.")
|
||||||
|
return
|
||||||
|
|
||||||
|
username = acc[0] # the Jellyfin username
|
||||||
|
|
||||||
|
# Reset the password
|
||||||
|
if reset_jellyfin_password(username, new_password):
|
||||||
|
await ctx.send(
|
||||||
|
f"✅ Your Jellyfin password for **{username}** has been reset!\n"
|
||||||
|
f"🌐 Login here: {JELLYFIN_URL}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await ctx.send(f"❌ Failed to reset password for **{username}**. Please contact an admin.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def deleteaccount(ctx, username: str):
|
||||||
|
if not isinstance(ctx.channel, discord.DMChannel):
|
||||||
|
await ctx.message.delete()
|
||||||
|
await ctx.send(f"{ctx.author.mention} Please DM me to delete your Jellyfin account.")
|
||||||
|
return
|
||||||
|
|
||||||
|
acc = get_account_by_discord(ctx.author.id)
|
||||||
|
if not acc or acc[0].lower() != username.lower():
|
||||||
|
await ctx.send("❌ That Jellyfin account is not linked to your Discord user.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if delete_jellyfin_user(username):
|
||||||
|
delete_account(ctx.author.id)
|
||||||
|
await ctx.send("✅ Account deleted.")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ Failed to delete account.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def syncaccounts(ctx):
|
||||||
|
guild = bot.get_guild(GUILD_ID)
|
||||||
|
removed = []
|
||||||
|
for discord_id, jf_username in get_accounts():
|
||||||
|
m = guild.get_member(discord_id)
|
||||||
|
if m is None or not has_required_role(m):
|
||||||
|
if delete_jellyfin_user(jf_username):
|
||||||
|
delete_account(discord_id)
|
||||||
|
removed.append(jf_username)
|
||||||
|
|
||||||
|
log_channel = bot.get_channel(SYNC_LOG_CHANNEL_ID)
|
||||||
|
if removed and log_channel:
|
||||||
|
await log_channel.send(f"🧹 Removed {len(removed)} Jellyfin accounts: {', '.join(removed)}")
|
||||||
|
|
||||||
|
await ctx.send("✅ Sync complete.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def searchaccount(ctx, username: str):
|
||||||
|
member = ctx.guild.get_member(ctx.author.id)
|
||||||
|
if not has_admin_role(member):
|
||||||
|
await ctx.send("❌ You don’t have permission to use this command.")
|
||||||
|
return
|
||||||
|
|
||||||
|
result = get_account_by_jellyfin(username)
|
||||||
|
if result:
|
||||||
|
discord_id = result[0]
|
||||||
|
user = await bot.fetch_user(discord_id)
|
||||||
|
await ctx.send(f"🔍 Jellyfin account **{username}** is linked to Discord user {user.mention}.")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ No linked Discord user found for that Jellyfin account.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def searchdiscord(ctx, user: discord.User):
|
||||||
|
member = ctx.guild.get_member(ctx.author.id)
|
||||||
|
if not has_admin_role(member):
|
||||||
|
await ctx.send("❌ You don’t have permission to use this command.")
|
||||||
|
return
|
||||||
|
|
||||||
|
result = get_account_by_discord(user.id)
|
||||||
|
if result:
|
||||||
|
await ctx.send(f"🔍 Discord user {user.mention} is linked to Jellyfin account **{result[0]}**.")
|
||||||
|
else:
|
||||||
|
await ctx.send("❌ That Discord user does not have a linked Jellyfin account.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def scanlibraries(ctx):
|
||||||
|
member = ctx.guild.get_member(ctx.author.id)
|
||||||
|
if not has_admin_role(member):
|
||||||
|
await ctx.send("❌ You don’t have permission to use this command.")
|
||||||
|
return
|
||||||
|
|
||||||
|
headers = {"X-Emby-Token": JELLYFIN_API_KEY}
|
||||||
|
response = requests.post(f"{JELLYFIN_URL}/Library/Refresh", headers=headers)
|
||||||
|
if response.status_code in (200, 204):
|
||||||
|
await ctx.send("✅ All Jellyfin libraries are being scanned.")
|
||||||
|
else:
|
||||||
|
await ctx.send(f"❌ Failed to start library scan. Status code: {response.status_code}")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def assignaccount(ctx, jellyfin_username: str, user: discord.User):
|
||||||
|
member = ctx.guild.get_member(ctx.author.id)
|
||||||
|
if not has_admin_role(member):
|
||||||
|
await ctx.send("❌ You don’t have permission to use this command.")
|
||||||
|
return
|
||||||
|
|
||||||
|
add_account(user.id, jellyfin_username)
|
||||||
|
await ctx.send(f"✅ Linked Jellyfin account **{jellyfin_username}** to {user.mention}.")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def setprefix(ctx, new_prefix: str):
|
||||||
|
"""Admin-only: change the bot command prefix"""
|
||||||
|
member = ctx.guild.get_member(ctx.author.id)
|
||||||
|
if not has_admin_role(member):
|
||||||
|
await ctx.send("❌ You don’t have permission to use this command.")
|
||||||
|
return
|
||||||
|
|
||||||
|
global PREFIX
|
||||||
|
PREFIX = new_prefix
|
||||||
|
bot.command_prefix = PREFIX # Update bot prefix dynamically
|
||||||
|
|
||||||
|
# Update .env file
|
||||||
|
env_file = ".env"
|
||||||
|
lines = []
|
||||||
|
with open(env_file, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith("PREFIX="):
|
||||||
|
lines.append(f"PREFIX={PREFIX}\n")
|
||||||
|
else:
|
||||||
|
lines.append(line)
|
||||||
|
with open(env_file, "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Command prefix has been updated to `{PREFIX}`")
|
||||||
|
|
||||||
|
@bot.command(name="help")
|
||||||
|
async def help_command(ctx):
|
||||||
|
member = ctx.guild.get_member(ctx.author.id)
|
||||||
|
is_admin = has_admin_role(member)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="📖 Jellyfin Bot Help",
|
||||||
|
description="Here are the available commands:",
|
||||||
|
color=discord.Color.blue()
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="User Commands", value=(
|
||||||
|
f"`{PREFIX}createaccount <username> <password>` - Create your Jellyfin account\n"
|
||||||
|
f"`{PREFIX}recoveraccount <newpassword>` - Reset your password\n"
|
||||||
|
f"`{PREFIX}deleteaccount <username>` - Delete your Jellyfin account\n"
|
||||||
|
), inline=False)
|
||||||
|
|
||||||
|
if is_admin:
|
||||||
|
embed.add_field(name="Admin Commands", value=(
|
||||||
|
f"`{PREFIX}syncaccounts` - Remove Jellyfin accounts from users without roles\n"
|
||||||
|
f"`{PREFIX}searchaccount <jellyfin_username>` - Find linked Discord user\n"
|
||||||
|
f"`{PREFIX}searchdiscord @user` - Find linked Jellyfin account\n"
|
||||||
|
f"`{PREFIX}scanlibraries` - Scan all Jellyfin libraries\n"
|
||||||
|
f"`{PREFIX}assignaccount <jellyfin_username> @user` - Manually link accounts\n"
|
||||||
|
), inline=False)
|
||||||
|
embed.add_field(name="Admin Bot Commands", value=(
|
||||||
|
f"`{PREFIX}setprefix` - Change the bots command prefix\n"
|
||||||
|
), inline=False)
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# TASKS
|
||||||
|
# =====================
|
||||||
|
@tasks.loop(hours=24)
|
||||||
|
async def daily_check():
|
||||||
|
guild = bot.get_guild(GUILD_ID)
|
||||||
|
removed = []
|
||||||
|
for discord_id, jf_username in get_accounts():
|
||||||
|
m = guild.get_member(discord_id)
|
||||||
|
if m is None or not has_required_role(m):
|
||||||
|
if delete_jellyfin_user(jf_username):
|
||||||
|
delete_account(discord_id)
|
||||||
|
removed.append(jf_username)
|
||||||
|
if removed:
|
||||||
|
print(f"Daily cleanup: removed {len(removed)} accounts: {removed}")
|
||||||
|
|
||||||
|
@bot.event
|
||||||
|
async def on_ready():
|
||||||
|
print(f"Logged in as {bot.user}")
|
||||||
|
init_db()
|
||||||
|
daily_check.start()
|
||||||
|
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name=f"{PREFIX}help"))
|
||||||
|
|
||||||
|
bot.run(TOKEN)
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
discord.py==2.3.2
|
||||||
|
requests==2.32.3
|
||||||
|
mysql-connector-python==9.0.0
|
||||||
|
python-dotenv==1.0.1
|
||||||
Reference in New Issue
Block a user