Merge pull request #12 from Yoruio/experimental

Experimental
This commit is contained in:
Roy Du
2022-07-16 15:04:52 -06:00
committed by GitHub
9 changed files with 135 additions and 34 deletions

View File

@@ -41,8 +41,6 @@ This command is used to remove a record from the Db. Use /membarr dbls to determ
2. Inside the Community Applications app store, search for Invitarr. 2. Inside the Community Applications app store, search for Invitarr.
3. Click the Install Button. 3. Click the Install Button.
4. On the following Add Container screen, Change repository to yoruio/invitarr:latest 4. On the following Add Container screen, Change repository to yoruio/invitarr:latest
> **Note**
> Membarr uses a slightly different client table than Invitarr, so existing app.db files will need to be deleted (this will also delete your users). See [Migration](#migration-from-invitarr) for migration info
5. Add discord bot token. 5. Add discord bot token.
6. Click apply 6. Click apply
7. Finish setting up using [Setup Commands](#after-bot-has-started) 7. Finish setting up using [Setup Commands](#after-bot-has-started)
@@ -93,8 +91,8 @@ This command disables the Plex integration (currently only disables auto-add / a
# Jellyfin Setup Commands: # Jellyfin Setup Commands:
``` ```
/jellyfinsettings setup <server url> <api key> /jellyfinsettings setup <server url> <api key> <optional: external server url (default: server url)>
This command is used to setup the Jellyfin server This command is used to setup the Jellyfin server. The external server URL is the URL that is sent to users to log into your Jellyfin server.
/jellyfinsettings addrole <@role> /jellyfinsettings addrole <@role>
These role(s) will be used as the role(s) to automatically invite user to Jellyfin These role(s) will be used as the role(s) to automatically invite user to Jellyfin
/jellyfinsettings removerole <@role> /jellyfinsettings removerole <@role>
@@ -108,7 +106,10 @@ This command disables the Jellyfin integration (currently only disables auto-add
``` ```
# Migration from Invitarr # Migration from Invitarr
Membarr uses a slightly different database table than Invitarr. To migrate users, you will need a sqlite database browser like DB Browser. Add a column to the sqlite table called "jellyfin_username", and make the "email" column nullable. Or, delete your existing app.db table and start fresh. Membarr uses a slightly different database table than Invitarr. Membarr will automatically update the Invitarr db table to the current Membarr table format, but the new table will no longer be compatible with Invitarr, so backup your app.db before running Membarr!
# Migration to Invitarr
As mentioned in [Migration from Invitarr](#Migration-From-Invitarr), Membarr has a slightly different db table than Invitarr. To Switch back to Invitarr, you will have to manually change the table format back. Open app.db in a sqlite cli tool or browser like DB Browser, then remove the "jellyfin_username" column, and make the "email" column non-nullable.
# Other stuff # Other stuff
**Enable Intents else bot will not Dm users after they get the role.** **Enable Intents else bot will not Dm users after they get the role.**

View File

@@ -1,5 +1,6 @@
from pickle import FALSE from pickle import FALSE
import app.bot.helper.jellyfinhelper as jelly import app.bot.helper.jellyfinhelper as jelly
from app.bot.helper.textformat import bcolors
import discord import discord
from discord.ext import commands from discord.ext import commands
from discord import app_commands from discord import app_commands
@@ -27,7 +28,6 @@ try:
PLEXPASS = config.get(BOT_SECTION, 'plex_pass') PLEXPASS = config.get(BOT_SECTION, 'plex_pass')
PLEX_SERVER_NAME = config.get(BOT_SECTION, 'plex_server_name') PLEX_SERVER_NAME = config.get(BOT_SECTION, 'plex_server_name')
except: except:
print("Could not load plex config")
plex_configured = False plex_configured = False
# Get Plex roles config # Get Plex roles config
@@ -91,9 +91,12 @@ except:
USE_PLEX = False USE_PLEX = False
try: try:
synced = not (float(config.get(BOT_SECTION, "sync_version")) < MEMBARR_VERSION) JELLYFIN_EXTERNAL_URL = config.get(BOT_SECTION, "jellyfin_external_url")
if not JELLYFIN_EXTERNAL_URL:
JELLYFIN_EXTERNAL_URL = JELLYFIN_SERVER_URL
except: except:
synced = False JELLYFIN_EXTERNAL_URL = JELLYFIN_SERVER_URL
print("Could not get Jellyfin external url. Defaulting to server url.")
if USE_PLEX and plex_configured: if USE_PLEX and plex_configured:
try: try:
@@ -120,9 +123,11 @@ class app(commands.Cog):
@commands.Cog.listener() @commands.Cog.listener()
async def on_ready(self): async def on_ready(self):
print('Made by Yoruio https://github.com/Yoruio/') print('------')
print('Forked from Invitarr https://github.com/Sleepingpirates/Invitarr') print("{:^41}".format(f"MEMBARR V {MEMBARR_VERSION}"))
print('Named by lordfransie') 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(f'Logged in as {self.bot.user} (ID: {self.bot.user.id})')
print('------') print('------')
@@ -299,7 +304,7 @@ class app(commands.Cog):
db.save_user_jellyfin(str(after.id), username) db.save_user_jellyfin(str(after.id), username)
await asyncio.sleep(5) await asyncio.sleep(5)
await embedcustom(after, "You have been added to Jellyfin!", {'Username': username, 'Password': f"||{password}||"}) await embedcustom(after, "You have been added to Jellyfin!", {'Username': username, 'Password': f"||{password}||"})
await embedinfo(after, f"Go to {JELLYFIN_SERVER_URL} to log in!") await embedinfo(after, f"Go to {JELLYFIN_EXTERNAL_URL} to log in!")
else: else:
await embedinfo(after, 'There was an error adding this user to Jellyfin. Message Server Admin.') await embedinfo(after, 'There was an error adding this user to Jellyfin. Message Server Admin.')
jellyfin_processed = True jellyfin_processed = True
@@ -447,4 +452,4 @@ class app(commands.Cog):
print(e) print(e)
async def setup(bot): async def setup(bot):
await bot.add_cog(app(bot)) await bot.add_cog(app(bot))

View File

@@ -12,7 +12,7 @@ config = configparser.ConfigParser()
CONFIG_KEYS = ['username', 'password', 'discord_bot_token', 'plex_user', 'plex_pass', CONFIG_KEYS = ['username', 'password', 'discord_bot_token', 'plex_user', 'plex_pass',
'plex_roles', 'plex_server_name', 'plex_libs', 'owner_id', 'channel_id', 'plex_roles', 'plex_server_name', 'plex_libs', 'owner_id', 'channel_id',
'auto_remove_user', 'jellyfin_api_key', 'jellyfin_server_url', 'jellyfin_roles', 'auto_remove_user', 'jellyfin_api_key', 'jellyfin_server_url', 'jellyfin_roles',
'jellyfin_libs', 'plex_enabled', 'jellyfin_enabled', 'sync_version'] 'jellyfin_libs', 'plex_enabled', 'jellyfin_enabled', 'jellyfin_external_url']
# settings # settings
Discord_bot_token = "" Discord_bot_token = ""
@@ -96,6 +96,14 @@ except:
print("Could not load Jellyfin config") print("Could not load Jellyfin config")
jellyfin_configured = False jellyfin_configured = 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.")
# Get Jellyfin roles config # Get Jellyfin roles config
try: try:
jellyfin_roles = config.get(BOT_SECTION, 'jellyfin_roles') jellyfin_roles = config.get(BOT_SECTION, 'jellyfin_roles')
@@ -133,12 +141,6 @@ except:
print("Could not get Plex enable config. Defaulting to False") print("Could not get Plex enable config. Defaulting to False")
USE_PLEX = False USE_PLEX = False
try:
synced = not (float(config.get(BOT_SECTION, "sync_version")) < MEMBARR_VERSION)
except:
print("Could not find previously synced version. Setting synced to false.")
synced = False
def get_config(): def get_config():
""" """
Function to return current config Function to return current config

View File

@@ -1,6 +1,9 @@
import sqlite3 import sqlite3
from app.bot.helper.dbupdater import check_table_version, update_table
DB_URL = 'app/config/app.db' DB_URL = 'app/config/app.db'
DB_TABLE = 'clients'
def create_connection(db_file): def create_connection(db_file):
""" create a database connection to a SQLite database """ """ create a database connection to a SQLite database """
@@ -26,7 +29,7 @@ def checkTableExists(dbcon, tablename):
conn = create_connection(DB_URL) conn = create_connection(DB_URL)
# Checking if table exists # Checking if table exists
if checkTableExists(conn, 'clients'): if checkTableExists(conn, DB_TABLE):
print('Table exists.') print('Table exists.')
else: else:
conn.execute( conn.execute(
@@ -38,6 +41,8 @@ else:
PRIMARY KEY("id" AUTOINCREMENT) PRIMARY KEY("id" AUTOINCREMENT)
);''') );''')
update_table(conn, DB_TABLE)
def save_user_email(username, email): def save_user_email(username, email):
if username and email: if username and email:
conn.execute(f""" conn.execute(f"""

View File

@@ -0,0 +1,65 @@
import sqlite3
CURRENT_VERSION = 'Membarr V1.1'
table_history = {
'Invitarr V1.0': [
(0, 'id', 'INTEGER', 1, None, 1),
(1, 'discord_username', 'TEXT', 1, None, 0),
(2, 'email', 'TEXT', 1, None, 0)
],
'Membarr V1.1': [
(0, 'id', 'INTEGER', 1, None, 1),
(1, 'discord_username', 'TEXT', 1, None, 0),
(2, 'email', 'TEXT', 0, None, 0),
(3, 'jellyfin_username', 'TEXT', 0, None, 0)
]
}
def check_table_version(conn, tablename):
dbcur = conn.cursor()
dbcur.execute(f"PRAGMA table_info({tablename})")
table_format = dbcur.fetchall()
for app_version in table_history:
if table_history[app_version] == table_format:
return app_version
raise ValueError("Could not identify database table version.")
def update_table(conn, tablename):
version = check_table_version(conn, tablename)
print('------')
print(f'DB table version: {version}')
if version == CURRENT_VERSION:
print('DB table up to date!')
print('------')
return
# Table NOT up to date.
# Update to Membarr V1.1 table
if version == 'Invitarr V1.0':
print("Upgrading DB table from Invitarr v1.0 to Membarr V1.1")
# Create temp table
conn.execute(
'''CREATE TABLE "membarr_temp_upgrade_table" (
"id" INTEGER NOT NULL UNIQUE,
"discord_username" TEXT NOT NULL UNIQUE,
"email" TEXT,
"jellyfin_username" TEXT,
PRIMARY KEY("id" AUTOINCREMENT)
);''')
conn.execute(f'''
INSERT INTO membarr_temp_upgrade_table(id, discord_username, email)
SELECT id, discord_username, email
FROM {tablename};
''')
conn.execute(f'''
DROP TABLE {tablename};
''')
conn.execute(f'''
ALTER TABLE membarr_temp_upgrade_table RENAME TO {tablename}
''')
conn.commit()
version = 'Membarr V1.1'
print('------')

View File

@@ -164,12 +164,12 @@ def get_config(jellyfin_url, jellyfin_api_key):
url = f"{jellyfin_url}/System/Configuration" url = f"{jellyfin_url}/System/Configuration"
querystring = {"api_key":jellyfin_api_key} querystring = {"api_key":jellyfin_api_key}
response = requests.request("GET", url, params=querystring) response = requests.request("GET", url, params=querystring, timeout=5)
return response.json() return response.json()
def get_status(jellyfin_url, jellyfin_api_key): def get_status(jellyfin_url, jellyfin_api_key):
url = f"{jellyfin_url}/System/Configuration" url = f"{jellyfin_url}/System/Configuration"
querystring = {"api_key":jellyfin_api_key} querystring = {"api_key":jellyfin_api_key}
response = requests.request("GET", url, params=querystring) response = requests.request("GET", url, params=querystring, timeout=5)
return response.status_code return response.status_code

View File

@@ -18,11 +18,11 @@ async def embedcustom(recipient, title, fields, ephemeral=True):
async def send_info(recipient, message, ephemeral=True): async def send_info(recipient, message, ephemeral=True):
if isinstance(recipient, discord.InteractionResponse): if isinstance(recipient, discord.InteractionResponse):
await recipient.send_message(message, ephemeral=ephemeral) await recipient.send_message(message, ephemeral=ephemeral)
elif isinstance(recipient, discord.User) or isinstance(recipient, discord.member.Member): elif isinstance(recipient, discord.User) or isinstance(recipient, discord.member.Member) or isinstance(recipient, discord.Webhook):
await recipient.send(message) await recipient.send(message)
async def send_embed(recipient, embed, ephemeral=True): async def send_embed(recipient, embed, ephemeral=True):
if isinstance(recipient, discord.User) or isinstance(recipient, discord.member.Member): if isinstance(recipient, discord.User) or isinstance(recipient, discord.member.Member) or isinstance(recipient, discord.Webhook):
await recipient.send(embed=embed) await recipient.send(embed=embed)
elif isinstance(recipient, discord.InteractionResponse): elif isinstance(recipient, discord.InteractionResponse):
await recipient.send_message(embed=embed, ephemeral = ephemeral) await recipient.send_message(embed=embed, ephemeral = ephemeral)

View File

@@ -0,0 +1,14 @@
from multiprocessing import AuthenticationError
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
AUTHOR = '\033[1;45;96m'

27
run.py
View File

@@ -11,14 +11,14 @@ from app.bot.helper.confighelper import MEMBARR_VERSION, switch, Discord_bot_tok
import app.bot.helper.confighelper as confighelper import app.bot.helper.confighelper as confighelper
import app.bot.helper.jellyfinhelper as jelly import app.bot.helper.jellyfinhelper as jelly
from app.bot.helper.message import * from app.bot.helper.message import *
from requests import ConnectTimeout
maxroles = 10 maxroles = 10
if switch == 0: if switch == 0:
print("Missing Config.") print("Missing Config.")
sys.exit() sys.exit()
print(f"V {MEMBARR_VERSION}")
class Bot(commands.Bot): class Bot(commands.Bot):
def __init__(self) -> None: def __init__(self) -> None:
print("Initializing Discord bot") print("Initializing Discord bot")
@@ -154,7 +154,8 @@ async def jellyrolels(interaction: discord.Interaction):
@jellyfin_commands.command(name="setup", description="Setup Jellyfin integration") @jellyfin_commands.command(name="setup", description="Setup Jellyfin integration")
@app_commands.checks.has_permissions(administrator=True) @app_commands.checks.has_permissions(administrator=True)
async def setupjelly(interaction: discord.Interaction, server_url: str, api_key: str): async def setupjelly(interaction: discord.Interaction, server_url: str, api_key: str, external_url: str = None):
await interaction.response.defer()
# get rid of training slashes # get rid of training slashes
server_url = server_url.rstrip('/') server_url = server_url.rstrip('/')
@@ -164,28 +165,36 @@ async def setupjelly(interaction: discord.Interaction, server_url: str, api_key:
pass pass
elif server_status == 401: elif server_status == 401:
# Unauthorized # Unauthorized
await embederror(interaction.response, "API key provided is invalid") await embederror(interaction.followup, "API key provided is invalid")
return return
elif server_status == 403: elif server_status == 403:
# Forbidden # Forbidden
await embederror(interaction.response, "API key provided does not have permissions") await embederror(interaction.followup, "API key provided does not have permissions")
return return
elif server_status == 404: elif server_status == 404:
# page not found # page not found
await embederror(interaction.response, "Server endpoint provided was not found") await embederror(interaction.followup, "Server endpoint provided was not found")
return return
else: else:
await embederror(interaction.response, "Unknown error occurred while connecting to server. Check Membarr logs.") await embederror(interaction.followup, "Unknown error occurred while connecting to Jellyfin. Check Membarr logs.")
except ConnectTimeout as e:
await embederror(interaction.followup, "Connection to server timed out. Check that Jellyfin is online and reachable.")
return
except Exception as e: except Exception as e:
print("Exception while testing Jellyfin connection") print("Exception while testing Jellyfin connection")
print(type(e).__name__)
print(e) print(e)
await embederror(interaction.response, "Could not connect to server. Check logs for more details.") await embederror(interaction.followup, "Unknown exception while connecting to Jellyfin. Check Membarr logs")
return return
confighelper.change_config("jellyfin_server_url", str(server_url)) confighelper.change_config("jellyfin_server_url", str(server_url))
confighelper.change_config("jellyfin_api_key", str(api_key)) confighelper.change_config("jellyfin_api_key", str(api_key))
if external_url is not None:
confighelper.change_config("jellyfin_external_url", str(external_url))
else:
confighelper.change_config("jellyfin_external_url", "")
print("Jellyfin server URL and API key updated. Restarting bot.") print("Jellyfin server URL and API key updated. Restarting bot.")
await interaction.response.send_message("Jellyfin server URL and API key updated. Restarting bot.", ephemeral=True) await interaction.followup.send("Jellyfin server URL and API key updated. Restarting bot.", ephemeral=True)
await reload() await reload()
print("Bot has been restarted. Give it a few seconds.") print("Bot has been restarted. Give it a few seconds.")