feat: Jellyfin integration finished

- Finsihed Jellyfin integration with auto-add and auto-remove
 - Integration toggle in config

task: none
This commit is contained in:
Yoruio
2022-07-09 17:43:52 -06:00
parent d2e184144b
commit fb105a2927
8 changed files with 665 additions and 89 deletions

View File

@@ -18,10 +18,14 @@ Invitarr is a chatbot that invites discord users to plex. You can also automate
Commands:
```
.plexinvite <email>
.plexinvite / .plexadd <email>
This command is used to add an email to plex
.plexremove <email>
.plexremove / .plexrm <email>
This command is used to remove an email from plex
.jellyfininvite / .jellyadd <username>
This command is used to add a user to Jellyfin.
.jellyremove / .jellyrm <username>
This command is used to remove a user from Jellyfin.
.dbls
This command is used to list Invitarrs database
.dbadd <@user> "<email>" "<jellyfinUsername>"
@@ -66,7 +70,7 @@ docker run -d --restart unless-stopped --name invitarr -v /path to config:/app/a
# After bot has started
# Setup Commands:
# Plex Setup Commands:
```
.setupplex
@@ -74,7 +78,21 @@ This command is used to setup plex login.
.plexroleadd <@role>
These role(s) will be used as the role(s) to automatically invite user to plex
.setupplexlibs (optional)
This command is used to setup plex libraries. Default is set to all.
This command is used to setup plex libraries. Default is set to all.
.plexdisable
This command disables the Plex integration (currently only disables auto-add / auto-remove)
```
# Jellyfin Setup Commands:
```
.setupjelly
This command is used to setup Jellyfin API.
.jellyroleadd <@role>
These role(s) will be used as the role(s) to automatically invite user to Jellyfin
.setupjellylibs (optional)
This command is used to setup jelly libraries. Default is set to all.
.jellydisable
this command disables the Jellyfin integration (currently only disables auto-add / auto-remove)
```
Refer to the [Wiki](https://github.com/Sleepingpirates/Invitarr/wiki) for detailed steps.

View File

@@ -1,3 +1,5 @@
from pickle import FALSE
import app.bot.helper.jellyfinhelper as jelly
import discord
from discord.ext import commands
import asyncio
@@ -5,6 +7,7 @@ from plexapi.myplex import MyPlexAccount
from discord import Webhook, AsyncWebhookAdapter
import app.bot.helper.db as db
import app.bot.helper.plexhelper as plexhelper
import app.bot.helper.jellyfinhelper as jelly
import texttable
import os
from os import path
@@ -19,28 +22,91 @@ PLEXPASS = ""
PLEX_SERVER_NAME = ""
Plex_LIBS = None
USE_PLEX = False
plex_configured = True
jellyfin_configured = True
if(path.exists('app/config/config.ini')):
if(path.exists(CONFIG_PATH)):
config = configparser.ConfigParser()
config.read(CONFIG_PATH)
# Get Plex config
try:
config = configparser.ConfigParser()
config.read(CONFIG_PATH)
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:
pass
if(path.exists('app/config/config.ini')):
print("Could not load plex config")
plex_configured = False
# Get Plex roles config
try:
plex_roles = config.get(BOT_SECTION, 'plex_roles')
except:
pass
if(path.exists('app/config/config.ini')):
print("Could not get Plex roles config")
plex_roles = None
if plex_roles is not None:
plex_roles = list(plex_roles.split(','))
else:
plex_roles = []
# Get Plex libs config
try:
Plex_LIBS = config.get(BOT_SECTION, 'plex_libs')
except:
pass
if USE_PLEX:
print("Could not get Plex libs config. Defaulting to all libraries.")
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:
print("Could not load Jellyfin config")
jellyfin_configured = False
# Get Jellyfin roles config
try:
jellyfin_roles = config.get(BOT_SECTION, 'jellyfin_roles')
except:
print("Could not get Jellyfin roles config")
jellyfin_roles = None
if jellyfin_roles is not None:
jellyfin_roles = list(jellyfin_roles.split(','))
else:
jellyfin_roles = []
# Get Jellyfin libs config
try:
jellyfin_libs = config.get(BOT_SECTION, 'jellyfin_libs')
except:
print("Could not get Jellyfin libs config. Defaulting to all libraries.")
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:
print("Could not get Jellyfin enable config. Defaulting to False")
USE_JELLYFIN = False
try:
USE_PLEX = config.get(BOT_SECTION, "plex_enabled")
USE_PLEX = USE_PLEX.lower() == "true"
except:
print("Could not get Plex enable config. Defaulting to False")
USE_PLEX = False
if USE_PLEX and plex_configured:
try:
account = MyPlexAccount(PLEXUSER, PLEXPASS)
plex = account.resource(PLEX_SERVER_NAME).connect() # returns a PlexServer instance
@@ -48,14 +114,9 @@ if USE_PLEX:
except Exception as e:
print('Error with plex login. Please check username and password and Plex server name or setup plex in the bot.')
print(f'Error: {e}')
if plex_roles is not None:
plex_roles = list(plex_roles.split(','))
if Plex_LIBS is None:
Plex_LIBS = ["all"]
else:
Plex_LIBS = list(Plex_LIBS.split(','))
print(f"Plex {'disabled' if not USE_PLEX else 'not configured'}. Skipping Plex login.")
class app(commands.Cog):
@@ -65,6 +126,7 @@ class app(commands.Cog):
@commands.Cog.listener()
async def on_ready(self):
print('Made by Sleepingpirate https://github.com/Sleepingpirates/')
print('Jellyfin implementation by Yoruio https://github.com/Yoruio/')
print(f'Logged in as {self.bot.user} (ID: {self.bot.user.id})')
print('------')
if plex_roles is None:
@@ -77,6 +139,12 @@ class app(commands.Cog):
async def embedinfo(self, author, message):
embed1 = discord.Embed(title=message, color=0x00F500)
await author.send(embed=embed1)
async def embedcustom(self, recipient, title, fields):
embed = discord.Embed(title=title)
for k in fields:
embed.add_field(name=str(k), value=str(fields[k]), inline=True)
await recipient.send(embed=embed)
async def getemail(self, after):
email = None
@@ -98,6 +166,31 @@ class app(commands.Cog):
message = "Timed Out. Message Server Admin with your email so They Can Add You Manually."
await self.embederror(after, message)
return None
async def getusername(self, after):
username = None
await self.embedinfo(after, f"Welcome To Jellyfin! Just reply with a username for Jellyfin so we can add you!")
await self.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 self.embederror(after, message)
continue
except asyncio.TimeoutError:
message = "Timed Out. Message Server Admin with your preferred username so They Can Add You Manually."
await self.embederror(after, message)
return None
except Exception as e:
await self.embederror(after, "Something went wrong. Please try again with another username.")
print (e)
username = None
async def addtoplex(self, email, channel):
@@ -123,6 +216,30 @@ class app(commands.Cog):
else:
await self.embederror(channel, 'Invalid email.')
return False
async def addtojellyfin(self, username, password, channel):
if not jelly.verify_username(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username):
await self.embederror(channel, f'An account with username {username} already exists.')
return
if jelly.add_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username, password, jellyfin_libs):
await self.embedinfo(channel, 'User successfully added to Jellyfin')
return True
else:
await self.embederror(channel, 'There was an error adding this user to Jellyfin. Check logs for more info.')
return False
async def removefromjellyfin(self, username, channel):
if jelly.verify_username(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username):
await self.embederror(channel, f'Could not find account with username {username}.')
return
if jelly.remove_user(JELLYFIN_SERVER_URL, JELLYFIN_API_KEY, username):
await self.embedinfo(channel, f'Successfully removed user {username} from Jellyfin.')
return True
else:
await self.embederror(channel, 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):
@@ -130,38 +247,95 @@ class app(commands.Cog):
return
roles_in_guild = after.guild.roles
role = None
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
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 self.embedinfo(after, "Got it we will be adding your email to plex shortly!")
if plexhelper.plexadd(plex,email,Plex_LIBS):
db.save_user(str(after.id), email)
await asyncio.sleep(5)
await self.embedinfo(after, 'You have Been Added To Plex! Login to plex and accept the invite!')
else:
await self.embedinfo(after, 'There was an error adding this email address. Message Server Admin.')
return
plex_processed = False
jellyfin_processed = False
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.delete_user(user_id)
if deleted:
print("Removed {} from db".format(email))
#await secure.send(plexname + ' ' + after.mention + ' was removed from plex')
else:
print("Cannot remove this user from db.")
except Exception as e:
print(e)
print("{} Cannot remove this user from plex.".format(email))
return
# 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 self.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 self.embedinfo(after, 'You have Been Added To Plex! Login to plex and accept the invite!')
else:
await self.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 self.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
# 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):
username = await self.getusername(after)
if username is not None:
await self.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 self.embedcustom(after, "You have been added to Jellyfin!", {'Username': username, 'Password': f"||{password}||"})
await self.embedinfo(after, f"Go to {JELLYFIN_SERVER_URL} to log in!")
else:
await self.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):
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 self.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):
@@ -181,6 +355,18 @@ class app(commands.Cog):
async def plexremove(self, ctx, email):
await self.removefromplex(email, ctx.channel)
@commands.has_permissions(administrator=True)
@commands.command(aliases=['jellyadd'])
async def jellyfininvite(self, ctx, username):
password = jelly.generate_password(16)
if await self.addtojellyfin(username, password, ctx.channel):
await self.embedcustom(ctx.author, "Jellyfin user created!", {'Username': username, 'Password': f"||{password}||"})
@commands.has_permissions(administrator=True)
@commands.command(aliases=['jellyrm'])
async def jellyfinremove(self, ctx, username):
await self.removefromjellyfin(username, ctx.channel)
@commands.has_permissions(administrator=True)
@commands.command()
async def dbadd(self, ctx, member: discord.Member, email, jellyfin_username):
@@ -240,32 +426,34 @@ class app(commands.Cog):
@commands.command()
async def dbrm(self, ctx, position):
embed = discord.Embed(title='Invitarr Database.')
all = db.read_useremail() # TODO: no need to read from DB or make a table here.
all = db.read_useremail()
table = texttable.Texttable()
table.set_cols_dtype(["t", "t", "t"])
table.set_cols_align(["c", "c", "c"])
header = ("#", "Name", "Email")
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]
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', inline=False)
table.add_row((index, username, dbemail))
embed.add_field(name=f"**{index}. {username}**", value=dbemail+'\n'+dbjellyfin+'\n', inline=False)
table.add_row((index, username, dbemail, dbjellyfin))
try:
position = int(position) - 1
id = all[position][1]
email = db.get_useremail(id)
discord_user = await self.bot.fetch_user(id)
username = discord_user.name
deleted = db.delete_user(id)
if deleted:
print("Removed {} from db".format(email))
await self.embedinfo(ctx.channel,"Removed {} from db".format(email))
print("Removed {} from db".format(username))
await self.embedinfo(ctx.channel,"Removed {} from db".format(username))
else:
await self.embederror(ctx.channel,"Cannot remove this user from db.")
except Exception as e:

View File

@@ -8,7 +8,8 @@ config = configparser.ConfigParser()
CONFIG_KEYS = ['username', 'password', 'discord_bot_token', 'plex_user', 'plex_pass',
'plex_roles', 'plex_server_name', 'plex_libs', 'owner_id', 'channel_id',
'auto_remove_user']
'auto_remove_user', 'jellyfin_api_key', 'jellyfin_server_url', 'jellyfin_roles',
'jellyfin_libs', 'plex_enabled', 'jellyfin_enabled']
# settings
Discord_bot_token = ""
@@ -17,6 +18,11 @@ PLEXUSER = ""
PLEXPASS = ""
PLEX_SERVER_NAME = ""
Plex_LIBS = None
JELLYFIN_SERVER_URL = ""
JELLYFIN_API_KEY = ""
jellyfin_libs = ""
jellyfin_roles = None
switch = 0
@@ -36,26 +42,63 @@ try:
except Exception as e:
pass
if(path.exists('app/config/config.ini')):
if(path.exists(CONFIG_PATH)):
config = configparser.ConfigParser()
config.read(CONFIG_PATH)
# Get Plex config
try:
config = configparser.ConfigParser()
config.read(CONFIG_PATH)
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:
pass
print("Could not load plex config")
if(path.exists('app/config/config.ini')):
# Get Plex roles config
try:
roles = config.get(BOT_SECTION, 'plex_roles')
plex_roles = config.get(BOT_SECTION, 'plex_roles')
except:
pass
if(path.exists('app/config/config.ini')):
print("Could not get Plex roles config")
# Get Plex libs config
try:
Plex_LIBS = config.get(BOT_SECTION, 'plex_libs')
except:
pass
print("Could not get Plex libs config")
# 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:
print("Could not load Jellyfin config")
# Get Jellyfin roles config
try:
jellyfin_roles = config.get(BOT_SECTION, 'jellyfin_roles')
except:
print("Could not get Jellyfin roles config")
# Get Jellyfin libs config
try:
jellyfin_libs = config.get(BOT_SECTION, 'jellyfin_libs')
except:
print("Could not get Jellyfin libs config")
# Get Enable config
try:
USE_JELLYFIN = config.get(BOT_SECTION, 'jellyfin_enabled')
except:
print("Could not get Jellyfin enable config. Defaulting to False")
USE_Jellyfin = False
try:
USE_PLEX = config.get(BOT_SECTION, "plex_enabled")
except:
print("Could not get Plex enable config. Defaulting to False")
USE_PLEX = False
def get_config():
"""
Function to return current config

View File

@@ -94,7 +94,7 @@ def get_useremail(username):
if email:
return email
else:
return "No users found"
return "No email found"
except:
return "error in fetching from db"
else:
@@ -122,6 +122,32 @@ def get_jellyfin_username(username):
else:
return "username cannot be empty"
def remove_email(username):
"""
Sets email of discord user to null in database
"""
if username:
conn.execute(f"UPDATE clients SET email = null WHERE discord_username = '{username}'")
conn.commit()
print(f"Email removed from user {username} in database")
return True
else:
print(f"Username cannot be empty.")
return False
def remove_jellyfin(username):
"""
Sets jellyfin username of discord user to null in database
"""
if username:
conn.execute(f"UPDATE clients SET jellyfin_username = null WHERE discord_username = '{username}'")
conn.commit()
print(f"Jellyfin username removed from user {username} in database")
return True
else:
print(f"Username cannot be empty.")
return False
def delete_user(username):
if username:

View File

@@ -0,0 +1,175 @@
import requests
import random
import string
def add_user(jellyfin_url, jellyfin_api_key, username, password, jellyfin_libs):
try:
url = f"{jellyfin_url}/Users/New"
querystring = {"api_key":jellyfin_api_key}
payload = {
"Name": username,
"Password": password
}
headers = {"Content-Type": "application/json"}
response = requests.request("POST", url, json=payload, headers=headers, params=querystring)
userId = response.json()["Id"]
if response.status_code != 200:
print(f"Error creating new Jellyfin user: {response.text}")
return False
# Grant access to User
url = f"{jellyfin_url}/Users/{userId}/Policy"
querystring = {"api_key":jellyfin_api_key}
enabled_folders = []
server_libs = get_libraries(jellyfin_url, jellyfin_api_key)
if jellyfin_libs[0] != "all":
for lib in jellyfin_libs:
found = False
for server_lib in server_libs:
if lib == server_lib['Name']:
enabled_folders.append(server_lib['ItemId'])
found = True
if not found:
print(f"Couldn't find Jellyfin Library: {lib}")
payload = {
"IsAdministrator": False,
"IsHidden": True,
"IsDisabled": False,
"BlockedTags": [],
"EnableUserPreferenceAccess": True,
"AccessSchedules": [],
"BlockUnratedItems": [],
"EnableRemoteControlOfOtherUsers": False,
"EnableSharedDeviceControl": True,
"EnableRemoteAccess": True,
"EnableLiveTvManagement": True,
"EnableLiveTvAccess": True,
"EnableMediaPlayback": True,
"EnableAudioPlaybackTranscoding": True,
"EnableVideoPlaybackTranscoding": True,
"EnablePlaybackRemuxing": True,
"ForceRemoteSourceTranscoding": False,
"EnableContentDeletion": False,
"EnableContentDeletionFromFolders": [],
"EnableContentDownloading": True,
"EnableSyncTranscoding": True,
"EnableMediaConversion": True,
"EnabledDevices": [],
"EnableAllDevices": True,
"EnabledChannels": [],
"EnableAllChannels": False,
"EnabledFolders": enabled_folders,
"EnableAllFolders": jellyfin_libs[0] == "all",
"InvalidLoginAttemptCount": 0,
"LoginAttemptsBeforeLockout": -1,
"MaxActiveSessions": 0,
"EnablePublicSharing": True,
"BlockedMediaFolders": [],
"BlockedChannels": [],
"RemoteClientBitrateLimit": 0,
"AuthenticationProviderId": "Jellyfin.Server.Implementations.Users.DefaultAuthenticationProvider",
"PasswordResetProviderId": "Jellyfin.Server.Implementations.Users.DefaultPasswordResetProvider",
"SyncPlayAccess": "CreateAndJoinGroups"
}
headers = {"content-type": "application/json"}
response = requests.request("POST", url, json=payload, headers=headers, params=querystring)
if response.status_code == 200 or response.status_code == 204:
return True
else:
print(f"Error setting user permissions: {response.text}")
except Exception as e:
print(e)
return False
def get_libraries(jellyfin_url, jellyfin_api_key):
url = f"{jellyfin_url}/Library/VirtualFolders"
querystring = {"api_key":jellyfin_api_key}
response = requests.request("GET", url, params=querystring)
return response.json()
def verify_username(jellyfin_url, jellyfin_api_key, username):
users = get_users(jellyfin_url, jellyfin_api_key)
valid = True
for user in users:
if user['Name'] == username:
valid = False
break
return valid
def remove_user(jellyfin_url, jellyfin_api_key, jellyfin_username):
try:
# Get User ID
users = get_users(jellyfin_url, jellyfin_api_key)
userId = None
for user in users:
if user['Name'].lower() == jellyfin_username.lower():
userId = user['Id']
if userId is None:
# User not found
print(f"Error removing user {jellyfin_username} from Jellyfin: Could not find user.")
return False
# Delete User
url = f"{jellyfin_url}/Users/{userId}"
querystring = {"api_key":jellyfin_api_key}
response = requests.request("DELETE", url, params=querystring)
if response.status_code == 204 or response.status_code == 200:
return True
else:
print(f"Error deleting Jellyfin user: {response.text}")
except Exception as e:
print(e)
return False
def get_users(jellyfin_url, jellyfin_api_key):
url = f"{jellyfin_url}/Users"
querystring = {"api_key":jellyfin_api_key}
response = requests.request("GET", url, params=querystring)
return response.json()
def generate_password(length, lower=True, upper=True, numbers=True, symbols=True):
character_list = []
if not (lower or upper or numbers or symbols):
raise ValueError("At least one character type must be provided")
if lower:
character_list += string.ascii_lowercase
if upper:
character_list += string.ascii_uppercase
if numbers:
character_list += string.digits
if symbols:
character_list += string.punctuation
return "".join(random.choice(character_list) for i in range(length))
def get_config(jellyfin_url, jellyfin_api_key):
url = f"{jellyfin_url}/System/Configuration"
querystring = {"api_key":jellyfin_api_key}
response = requests.request("GET", url, params=querystring)
return response.json()
def get_status(jellyfin_url, jellyfin_api_key):
url = f"{jellyfin_url}/System/Configuration"
querystring = {"api_key":jellyfin_api_key}
response = requests.request("GET", url, params=querystring)
return response.status_code

View File

@@ -46,7 +46,4 @@ def plexremoveinvite(plex, plexname):
def verifyemail(addressToVerify):
regex = '^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$'
match = re.match(regex, addressToVerify.lower())
if match == None:
return False
else:
return True
return match != None

View File

@@ -3,3 +3,5 @@ plex.py==0.9.0
PlexAPI==4.0.0
texttable
python-dotenv
jellyfin-apiclient-python
requests

153
run.py
View File

@@ -4,8 +4,9 @@ from discord.ext import commands, tasks
from discord.utils import get
import asyncio
import sys
from app.bot.helper.confighelper import switch, Discord_bot_token, plex_roles
from app.bot.helper.confighelper import switch, Discord_bot_token, plex_roles, jellyfin_roles
import app.bot.helper.confighelper as confighelper
import app.bot.helper.jellyfinhelper as jelly
maxroles = 10
print(f"Discord Bot Token: {Discord_bot_token}")
@@ -15,11 +16,16 @@ if plex_roles is None:
else:
plex_roles = list(plex_roles.split(','))
if jellyfin_roles is None:
jellyfin_roles = []
else:
jellyfin_roles = list(jellyfin_roles.split(','))
if switch == 0:
print("Missing Config.")
sys.exit()
print("V 1.0")
print("V 1.1")
intents = discord.Intents.default()
intents.members = True
@@ -39,12 +45,21 @@ async def on_message(message):
return
await bot.process_commands(message)
# these were copied from the app object. They could be made static instead but I'm lazy.
async def embederror(author, message):
embed1 = discord.Embed(title="ERROR",description=message, color=0xf50000)
await author.send(embed=embed1)
async def embedinfo(author, message):
embed1 = discord.Embed(title=message, color=0x00F500)
await author.send(embed=embed1)
def reload():
bot.reload_extension(f'app.bot.cogs.app')
async def getplex(ctx, type):
async def getuser(ctx, server, type):
value = None
await ctx.author.send("Please reply with your Plex {}:".format(type))
await ctx.author.send("Please reply with your {} {}:".format(server, type))
while(value == None):
def check(m):
return m.author == ctx.author and not m.guild
@@ -71,18 +86,18 @@ async def plexroleadd(ctx, role: discord.Role):
@bot.command()
@commands.has_permissions(administrator=True)
async def setupplex(ctx):
username = ""
pasword = ""
servername = ""
username = await getplex(ctx, "username")
username = None
password = None
servername = None
username = await getuser(ctx, "Plex", "username")
if username is None:
return
else:
password = await getplex(ctx, "password")
password = await getuser(ctx, "Plex", "password")
if password is None:
return
else:
servername = await getplex(ctx, "servername")
servername = await getuser(ctx, "Plex", "servername")
if servername is None:
return
else:
@@ -95,19 +110,131 @@ async def setupplex(ctx):
await ctx.author.send("Bot has been restarted. Give it a few seconds. Please check logs and make sure you see the line: `Logged into plex`. If not run this command again and make sure you enter the right values. ")
print("Bot has been restarted. Give it a few seconds.")
@bot.command()
@commands.has_permissions(administrator=True)
async def jellyroleadd(ctx, role: discord.Role):
if len(jellyfin_roles) <= maxroles:
jellyfin_roles.append(role.name)
print (f"new jellyfin roles: {jellyfin_roles}")
saveroles = ",".join(jellyfin_roles)
print (f"saveroles: {saveroles}")
confighelper.change_config("jellyfin_roles", saveroles)
await ctx.author.send("Updated Jellyfin roles. Bot is restarting. Please wait.")
print("Jellyfin roles updated. Restarting bot.")
reload()
await ctx.author.send("Bot has been restarted. Give it a few seconds.")
print("Bot has been restarted. Give it a few seconds.")
@bot.command()
@commands.has_permissions(administrator=True)
async def setupjelly(ctx):
jellyfin_api_key = None
jellyfin_server_url = None
jellyfin_server_url = await getuser(ctx, "Jellyfin", "Server Url")
if jellyfin_server_url is None:
return
jellyfin_api_key = await getuser(ctx, "Jellyfin", "API Key")
if jellyfin_api_key is None:
return
try:
server_status = jelly.get_status(jellyfin_server_url, jellyfin_api_key)
if server_status == 200:
pass
elif server_status == 401:
# Unauthorized
await embederror(ctx.author, "API key provided is invalid")
return
elif server_status == 403:
# Forbidden
await embederror(ctx.author, "API key provided does not have permissions")
return
elif server_status == 404:
# page not found
await embederror(ctx.author, "Server endpoint provided was not found")
return
except Exception as e:
print("Exception while testing Jellyfin connection")
print(e)
await embederror(ctx.author, "Could not connect to server. Check logs for more details.")
return
jellyfin_server_url = jellyfin_server_url.rstrip('/')
confighelper.change_config("jellyfin_server_url", str(jellyfin_server_url))
confighelper.change_config("jellyfin_api_key", str(jellyfin_api_key))
print("Jellyfin server URL and API key updated. Restarting bot.")
await ctx.author.send("Jellyfin server URL and API key updated. Restarting bot.")
reload()
await ctx.author.send("Bot has been restarted. Give it a few seconds. Please check logs and make sure you see the line: `Connected to Jellyfin`. If not run this command again and make sure you enter the right values. ")
print("Bot has been restarted. Give it a few seconds.")
@bot.command()
@commands.has_permissions(administrator=True)
async def setupplexlibs(ctx):
libs = ""
libs = await getplex(ctx, "libs")
libs = await getuser(ctx, "Plex", "libs")
if libs is None:
return
else:
confighelper.change_config("plex_libs", str(libs))
print("Plex libraries updated. Restarting bot. Please wait.")
reload()
await ctx.author.send("Bot has been restarted. Give it a few seconds. Please check logs and make sure you see the line: `Logged into plex`. If not run this command again and make sure you enter the right values. ")
await ctx.author.send("Bot has been restarted. Give it a few seconds.")
print("Bot has been restarted. Give it a few seconds.")
@bot.command()
@commands.has_permissions(administrator=True)
async def setupjellylibs(ctx):
libs = await getuser(ctx, "Jellyfin", "libs")
if libs is None:
return
else:
confighelper.change_config("jellyfin_libs", str(libs))
print("Jellyfin libraries updated. Restarting bot. Please wait.")
reload()
await ctx.author.send("Bot has been restarted. Give it a few seconds.")
print("Bot has been restarted. Give it a few seconds.")
# Enable / Disable Plex integration
@bot.command(aliases=["plexenable"])
@commands.has_permissions(administrator=True)
async def enableplex(ctx):
confighelper.change_config("plex_enabled", True)
print("Plex enabled, reloading server")
reload()
await ctx.author.send("Bot has restarted. Give it a few seconds.")
print("Bot has restarted. Give it a few seconds.")
@bot.command(aliases=["plexdisable"])
@commands.has_permissions(administrator=True)
async def disableplex(ctx):
confighelper.change_config("plex_enabled", False)
print("Plex disabled, reloading server")
reload()
await ctx.author.send("Bot has restarted. Give it a few seconds.")
print("Bot has restarted. Give it a few seconds.")
# Enable / Disable Jellyfin integration
@bot.command(aliases=["jellyenable"])
@commands.has_permissions(administrator=True)
async def enablejellyfin(ctx):
confighelper.change_config("jellyfin_enabled", True)
print("Jellyfin enabled, reloading server")
reload()
await ctx.author.send("Bot has restarted. Give it a few seconds.")
print("Bot has restarted. Give it a few seconds.")
@bot.command(aliases=["jellydisable"])
@commands.has_permissions(administrator=True)
async def disablejellyfin(ctx):
confighelper.change_config("jellyfin_enabled", False)
print("Jellyfin disabled, reloading server")
reload()
await ctx.author.send("Bot has restarted. Give it a few seconds.")
print("Bot has restarted. Give it a few seconds.")
bot.load_extension(f'app.bot.cogs.app')
bot.run(Discord_bot_token)