- Adds Jellyfin external URL config option - URL to send to users if different from server URL. - Adds Jellyfin server setup timeout after 5 seconds, and response delaying so discord bot doesn't timeout task: gh-13
456 lines
20 KiB
Python
456 lines
20 KiB
Python
from pickle import FALSE
|
|
import app.bot.helper.jellyfinhelper as jelly
|
|
from app.bot.helper.textformat import bcolors
|
|
import discord
|
|
from discord.ext import commands
|
|
from discord import app_commands
|
|
import asyncio
|
|
from plexapi.myplex import MyPlexAccount
|
|
import app.bot.helper.db as db
|
|
import app.bot.helper.plexhelper as plexhelper
|
|
import app.bot.helper.jellyfinhelper as jelly
|
|
import texttable
|
|
from app.bot.helper.message import *
|
|
from app.bot.helper.confighelper import *
|
|
|
|
CONFIG_PATH = 'app/config/config.ini'
|
|
BOT_SECTION = 'bot_envs'
|
|
|
|
plex_configured = True
|
|
jellyfin_configured = True
|
|
|
|
config = configparser.ConfigParser()
|
|
config.read(CONFIG_PATH)
|
|
|
|
# Get Plex config
|
|
try:
|
|
PLEXUSER = config.get(BOT_SECTION, 'plex_user')
|
|
PLEXPASS = config.get(BOT_SECTION, 'plex_pass')
|
|
PLEX_SERVER_NAME = config.get(BOT_SECTION, 'plex_server_name')
|
|
except:
|
|
plex_configured = False
|
|
|
|
# Get Plex roles config
|
|
try:
|
|
plex_roles = config.get(BOT_SECTION, 'plex_roles')
|
|
except:
|
|
plex_roles = None
|
|
if plex_roles:
|
|
plex_roles = list(plex_roles.split(','))
|
|
else:
|
|
plex_roles = []
|
|
|
|
# Get Plex libs config
|
|
try:
|
|
Plex_LIBS = config.get(BOT_SECTION, 'plex_libs')
|
|
except:
|
|
Plex_LIBS = None
|
|
if Plex_LIBS is None:
|
|
Plex_LIBS = ["all"]
|
|
else:
|
|
Plex_LIBS = list(Plex_LIBS.split(','))
|
|
|
|
# Get Jellyfin config
|
|
try:
|
|
JELLYFIN_SERVER_URL = config.get(BOT_SECTION, 'jellyfin_server_url')
|
|
JELLYFIN_API_KEY = config.get(BOT_SECTION, "jellyfin_api_key")
|
|
except:
|
|
jellyfin_configured = False
|
|
|
|
# Get Jellyfin roles config
|
|
try:
|
|
jellyfin_roles = config.get(BOT_SECTION, 'jellyfin_roles')
|
|
except:
|
|
jellyfin_roles = None
|
|
if jellyfin_roles:
|
|
jellyfin_roles = list(jellyfin_roles.split(','))
|
|
else:
|
|
jellyfin_roles = []
|
|
|
|
# Get Jellyfin libs config
|
|
try:
|
|
jellyfin_libs = config.get(BOT_SECTION, 'jellyfin_libs')
|
|
except:
|
|
jellyfin_libs = None
|
|
if jellyfin_libs is None:
|
|
jellyfin_libs = ["all"]
|
|
else:
|
|
jellyfin_libs = list(jellyfin_libs.split(','))
|
|
|
|
# Get Enable config
|
|
try:
|
|
USE_JELLYFIN = config.get(BOT_SECTION, 'jellyfin_enabled')
|
|
USE_JELLYFIN = USE_JELLYFIN.lower() == "true"
|
|
except:
|
|
USE_JELLYFIN = False
|
|
|
|
try:
|
|
USE_PLEX = config.get(BOT_SECTION, "plex_enabled")
|
|
USE_PLEX = USE_PLEX.lower() == "true"
|
|
except:
|
|
USE_PLEX = False
|
|
|
|
try:
|
|
JELLYFIN_EXTERNAL_URL = config.get(BOT_SECTION, "jellyfin_external_url")
|
|
if not JELLYFIN_EXTERNAL_URL:
|
|
JELLYFIN_EXTERNAL_URL = JELLYFIN_SERVER_URL
|
|
except:
|
|
JELLYFIN_EXTERNAL_URL = JELLYFIN_SERVER_URL
|
|
print("Could not get Jellyfin external url. Defaulting to server url.")
|
|
|
|
if USE_PLEX and plex_configured:
|
|
try:
|
|
print("Connecting to Plex......")
|
|
account = MyPlexAccount(PLEXUSER, PLEXPASS)
|
|
plex = account.resource(PLEX_SERVER_NAME).connect() # returns a PlexServer instance
|
|
print('Logged into plex!')
|
|
except Exception as e:
|
|
# probably rate limited.
|
|
print('Error with plex login. Please check username and password and Plex server name or setup plex in the bot. If you have restarted the bot multiple times recently, this is most likely due to being ratelimited on the Plex API. Try again in 10 minutes.')
|
|
print(f'Error: {e}')
|
|
else:
|
|
print(f"Plex {'disabled' if not USE_PLEX else 'not configured'}. Skipping Plex login.")
|
|
|
|
|
|
class app(commands.Cog):
|
|
# App command groups
|
|
plex_commands = app_commands.Group(name="plex", description="Membarr Plex commands")
|
|
jellyfin_commands = app_commands.Group(name="jellyfin", description="Membarr Jellyfin commands")
|
|
membarr_commands = app_commands.Group(name="membarr", description="Membarr general commands")
|
|
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
@commands.Cog.listener()
|
|
async def on_ready(self):
|
|
print('------')
|
|
print("{:^41}".format(f"MEMBARR V {MEMBARR_VERSION}"))
|
|
print(f'Made by Yoruio https://github.com/Yoruio/\n')
|
|
print(f'Forked from Invitarr https://github.com/Sleepingpirates/Invitarr')
|
|
print(f'Named by lordfransie')
|
|
print(f'Logged in as {self.bot.user} (ID: {self.bot.user.id})')
|
|
print('------')
|
|
|
|
# TODO: Make these debug statements work. roles are currently empty arrays if no roles assigned.
|
|
if plex_roles is None:
|
|
print('Configure Plex roles to enable auto invite to Plex after a role is assigned.')
|
|
if jellyfin_roles is None:
|
|
print('Configure Jellyfin roles to enable auto invite to Jellyfin after a role is assigned.')
|
|
|
|
async def getemail(self, after):
|
|
email = None
|
|
await embedinfo(after,'Welcome To '+ PLEX_SERVER_NAME +'. Just reply with your email so we can add you to Plex!')
|
|
await embedinfo(after,'I will wait 24 hours for your message, if you do not send it by then I will cancel the command.')
|
|
while(email == None):
|
|
def check(m):
|
|
return m.author == after and not m.guild
|
|
try:
|
|
email = await self.bot.wait_for('message', timeout=86400, check=check)
|
|
if(plexhelper.verifyemail(str(email.content))):
|
|
return str(email.content)
|
|
else:
|
|
email = None
|
|
message = "Invalid email. Please just type in your email and nothing else."
|
|
await embederror(after, message)
|
|
continue
|
|
except asyncio.TimeoutError:
|
|
message = "Timed Out. Message Server Admin with your email so They Can Add You Manually."
|
|
await embederror(after, message)
|
|
return None
|
|
|
|
async def getusername(self, after):
|
|
username = None
|
|
await embedinfo(after, f"Welcome To Jellyfin! Just reply with a username for Jellyfin so we can add you!")
|
|
await embedinfo(after, f"I will wait 24 hours for your message, if you do not send it by then I will cancel the command.")
|
|
while (username is None):
|
|
def check(m):
|
|
return m.author == after and not m.guild
|
|
try:
|
|
username = await self.bot.wait_for('message', timeout=86400, check=check)
|
|
if(jelly.verify_username(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, str(username.content))):
|
|
return str(username.content)
|
|
else:
|
|
username = None
|
|
message = "This username is already choosen. Please select another Username."
|
|
await embederror(after, message)
|
|
continue
|
|
except asyncio.TimeoutError:
|
|
message = "Timed Out. Message Server Admin with your preferred username so They Can Add You Manually."
|
|
print("Jellyfin user prompt timed out")
|
|
await embederror(after, message)
|
|
return None
|
|
except Exception as e:
|
|
await embederror(after, "Something went wrong. Please try again with another username.")
|
|
print (e)
|
|
username = None
|
|
|
|
|
|
async def addtoplex(self, email, response):
|
|
if(plexhelper.verifyemail(email)):
|
|
if plexhelper.plexadd(plex,email,Plex_LIBS):
|
|
await embedinfo(response, 'This email address has been added to plex')
|
|
return True
|
|
else:
|
|
await embederror(response, 'There was an error adding this email address. Check logs.')
|
|
return False
|
|
else:
|
|
await embederror(response, 'Invalid email.')
|
|
return False
|
|
|
|
async def removefromplex(self, email, response):
|
|
if(plexhelper.verifyemail(email)):
|
|
if plexhelper.plexremove(plex,email):
|
|
await embedinfo(response, 'This email address has been removed from plex.')
|
|
return True
|
|
else:
|
|
await embederror(response, 'There was an error removing this email address. Check logs.')
|
|
return False
|
|
else:
|
|
await embederror(response, 'Invalid email.')
|
|
return False
|
|
|
|
async def addtojellyfin(self, username, password, response):
|
|
if not jelly.verify_username(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username):
|
|
await embederror(response, f'An account with username {username} already exists.')
|
|
return False
|
|
|
|
if jelly.add_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username, password, jellyfin_libs):
|
|
return True
|
|
else:
|
|
await embederror(response, 'There was an error adding this user to Jellyfin. Check logs for more info.')
|
|
return False
|
|
|
|
async def removefromjellyfin(self, username, response):
|
|
if jelly.verify_username(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username):
|
|
await embederror(response, f'Could not find account with username {username}.')
|
|
return
|
|
|
|
if jelly.remove_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username):
|
|
await embedinfo(response, f'Successfully removed user {username} from Jellyfin.')
|
|
return True
|
|
else:
|
|
await embederror(response, f'There was an error removing this user from Jellyfin. Check logs for more info.')
|
|
return False
|
|
|
|
@commands.Cog.listener()
|
|
async def on_member_update(self, before, after):
|
|
if plex_roles is None and jellyfin_roles is None:
|
|
return
|
|
roles_in_guild = after.guild.roles
|
|
role = None
|
|
|
|
plex_processed = False
|
|
jellyfin_processed = False
|
|
|
|
# Check Plex roles
|
|
if plex_configured and USE_PLEX:
|
|
for role_for_app in plex_roles:
|
|
for role_in_guild in roles_in_guild:
|
|
if role_in_guild.name == role_for_app:
|
|
role = role_in_guild
|
|
|
|
# Plex role was added
|
|
if role is not None and (role in after.roles and role not in before.roles):
|
|
email = await self.getemail(after)
|
|
if email is not None:
|
|
await embedinfo(after, "Got it we will be adding your email to plex shortly!")
|
|
if plexhelper.plexadd(plex,email,Plex_LIBS):
|
|
db.save_user_email(str(after.id), email)
|
|
await asyncio.sleep(5)
|
|
await embedinfo(after, 'You have Been Added To Plex! Login to plex and accept the invite!')
|
|
else:
|
|
await embedinfo(after, 'There was an error adding this email address. Message Server Admin.')
|
|
plex_processed = True
|
|
break
|
|
|
|
# Plex role was removed
|
|
elif role is not None and (role not in after.roles and role in before.roles):
|
|
try:
|
|
user_id = after.id
|
|
email = db.get_useremail(user_id)
|
|
plexhelper.plexremove(plex,email)
|
|
deleted = db.remove_email(user_id)
|
|
if deleted:
|
|
print("Removed Plex email {} from db".format(after.name))
|
|
#await secure.send(plexname + ' ' + after.mention + ' was removed from plex')
|
|
else:
|
|
print("Cannot remove Plex from this user.")
|
|
await embedinfo(after, "You have been removed from Plex")
|
|
except Exception as e:
|
|
print(e)
|
|
print("{} Cannot remove this user from plex.".format(email))
|
|
plex_processed = True
|
|
break
|
|
if plex_processed:
|
|
break
|
|
|
|
role = None
|
|
# Check Jellyfin roles
|
|
if jellyfin_configured and USE_JELLYFIN:
|
|
for role_for_app in jellyfin_roles:
|
|
for role_in_guild in roles_in_guild:
|
|
if role_in_guild.name == role_for_app:
|
|
role = role_in_guild
|
|
|
|
# Jellyfin role was added
|
|
if role is not None and (role in after.roles and role not in before.roles):
|
|
print("Jellyfin role added")
|
|
username = await self.getusername(after)
|
|
print("Username retrieved from user")
|
|
if username is not None:
|
|
await embedinfo(after, "Got it we will be creating your Jellyfin account shortly!")
|
|
password = jelly.generate_password(16)
|
|
if jelly.add_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username, password, jellyfin_libs):
|
|
db.save_user_jellyfin(str(after.id), username)
|
|
await asyncio.sleep(5)
|
|
await embedcustom(after, "You have been added to Jellyfin!", {'Username': username, 'Password': f"||{password}||"})
|
|
await embedinfo(after, f"Go to {JELLYFIN_EXTERNAL_URL} to log in!")
|
|
else:
|
|
await embedinfo(after, 'There was an error adding this user to Jellyfin. Message Server Admin.')
|
|
jellyfin_processed = True
|
|
break
|
|
|
|
# Jellyfin role was removed
|
|
elif role is not None and (role not in after.roles and role in before.roles):
|
|
print("Jellyfin role removed")
|
|
try:
|
|
user_id = after.id
|
|
username = db.get_jellyfin_username(user_id)
|
|
jelly.remove_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username)
|
|
deleted = db.remove_jellyfin(user_id)
|
|
if deleted:
|
|
print("Removed Jellyfin from {}".format(after.name))
|
|
#await secure.send(plexname + ' ' + after.mention + ' was removed from plex')
|
|
else:
|
|
print("Cannot remove Jellyfin from this user")
|
|
await embedinfo(after, "You have been removed from Jellyfin")
|
|
except Exception as e:
|
|
print(e)
|
|
print("{} Cannot remove this user from Jellyfin.".format(username))
|
|
jellyfin_processed = True
|
|
break
|
|
if jellyfin_processed:
|
|
break
|
|
|
|
@commands.Cog.listener()
|
|
async def on_member_remove(self, member):
|
|
if USE_PLEX and plex_configured:
|
|
email = db.get_useremail(member.id)
|
|
plexhelper.plexremove(plex,email)
|
|
|
|
if USE_JELLYFIN and jellyfin_configured:
|
|
jellyfin_username = db.get_jellyfin_username(member.id)
|
|
jelly.remove_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, jellyfin_username)
|
|
|
|
deleted = db.delete_user(member.id)
|
|
if deleted:
|
|
print("Removed {} from db because user left discord server.".format(email))
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@plex_commands.command(name="invite", description="Invite a user to Plex")
|
|
async def plexinvite(self, interaction: discord.Interaction, email: str):
|
|
await self.addtoplex(email, interaction.response)
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@plex_commands.command(name="remove", description="Remove a user from Plex")
|
|
async def plexremove(self, interaction: discord.Interaction, email: str):
|
|
await self.removefromplex(email, interaction.response)
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@jellyfin_commands.command(name="invite", description="Invite a user to Jellyfin")
|
|
async def jellyfininvite(self, interaction: discord.Interaction, username: str):
|
|
password = jelly.generate_password(16)
|
|
if await self.addtojellyfin(username, password, interaction.response):
|
|
await embedcustom(interaction.response, "Jellyfin user created!", {'Username': username, 'Password': f"||{password}||"})
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@jellyfin_commands.command(name="remove", description="Remove a user from Jellyfin")
|
|
async def jellyfinremove(self, interaction: discord.Interaction, username: str):
|
|
await self.removefromjellyfin(username, interaction.response)
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@membarr_commands.command(name="dbadd", description="Add a user to the Membarr database")
|
|
async def dbadd(self, interaction: discord.Interaction, member: discord.Member, email: str = "", jellyfin_username: str = ""):
|
|
email = email.strip()
|
|
jellyfin_username = jellyfin_username.strip()
|
|
|
|
# Check email if provided
|
|
if email and not plexhelper.verifyemail(email):
|
|
await embederror(interaction.response, "Invalid email.")
|
|
return
|
|
|
|
try:
|
|
db.save_user_all(str(member.id), email, jellyfin_username)
|
|
await embedinfo(interaction.response,'User was added to the database.')
|
|
except Exception as e:
|
|
await embedinfo(interaction.response, 'There was an error adding this user to database. Check Membarr logs for more info')
|
|
print(e)
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@membarr_commands.command(name="dbls", description="View Membarr database")
|
|
async def dbls(self, interaction: discord.Interaction):
|
|
|
|
embed = discord.Embed(title='Membarr Database.')
|
|
all = db.read_all()
|
|
table = texttable.Texttable()
|
|
table.set_cols_dtype(["t", "t", "t", "t"])
|
|
table.set_cols_align(["c", "c", "c", "c"])
|
|
header = ("#", "Name", "Email", "Jellyfin")
|
|
table.add_row(header)
|
|
for index, peoples in enumerate(all):
|
|
index = index + 1
|
|
id = int(peoples[1])
|
|
dbuser = self.bot.get_user(id)
|
|
dbemail = peoples[2] if peoples[2] else "No Plex"
|
|
dbjellyfin = peoples[3] if peoples[3] else "No Jellyfin"
|
|
try:
|
|
username = dbuser.name
|
|
except:
|
|
username = "User Not Found."
|
|
embed.add_field(name=f"**{index}. {username}**", value=dbemail+'\n'+dbjellyfin+'\n', inline=False)
|
|
table.add_row((index, username, dbemail, dbjellyfin))
|
|
|
|
total = str(len(all))
|
|
if(len(all)>25):
|
|
f = open("db.txt", "w")
|
|
f.write(table.draw())
|
|
f.close()
|
|
await interaction.response.send_message("Database too large! Total: {total}".format(total = total),file=discord.File('db.txt'), ephemeral=True)
|
|
else:
|
|
await interaction.response.send_message(embed = embed, ephemeral=True)
|
|
|
|
|
|
@app_commands.checks.has_permissions(administrator=True)
|
|
@membarr_commands.command(name="dbrm", description="Remove user from Membarr database")
|
|
async def dbrm(self, interaction: discord.Interaction, position: int):
|
|
embed = discord.Embed(title='Membarr Database.')
|
|
all = db.read_all()
|
|
for index, peoples in enumerate(all):
|
|
index = index + 1
|
|
id = int(peoples[1])
|
|
dbuser = self.bot.get_user(id)
|
|
dbemail = peoples[2] if peoples[2] else "No Plex"
|
|
dbjellyfin = peoples[3] if peoples[3] else "No Jellyfin"
|
|
try:
|
|
username = dbuser.name
|
|
except:
|
|
username = "User Not Found."
|
|
embed.add_field(name=f"**{index}. {username}**", value=dbemail+'\n'+dbjellyfin+'\n', inline=False)
|
|
|
|
try:
|
|
position = int(position) - 1
|
|
id = all[position][1]
|
|
discord_user = await self.bot.fetch_user(id)
|
|
username = discord_user.name
|
|
deleted = db.delete_user(id)
|
|
if deleted:
|
|
print("Removed {} from db".format(username))
|
|
await embedinfo(interaction.response,"Removed {} from db".format(username))
|
|
else:
|
|
await embederror(interaction.response,"Cannot remove this user from db.")
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
async def setup(bot):
|
|
await bot.add_cog(app(bot))
|