11
README.md
11
README.md
@@ -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.
|
||||
3. Click the Install Button.
|
||||
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.
|
||||
6. Click apply
|
||||
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:
|
||||
```
|
||||
/jellyfinsettings setup <server url> <api key>
|
||||
This command is used to setup the Jellyfin server
|
||||
/jellyfinsettings setup <server url> <api key> <optional: external server url (default: server url)>
|
||||
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>
|
||||
These role(s) will be used as the role(s) to automatically invite user to Jellyfin
|
||||
/jellyfinsettings removerole <@role>
|
||||
@@ -108,7 +106,10 @@ This command disables the Jellyfin integration (currently only disables auto-add
|
||||
```
|
||||
|
||||
# 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
|
||||
**Enable Intents else bot will not Dm users after they get the role.**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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
|
||||
@@ -27,7 +28,6 @@ try:
|
||||
PLEXPASS = config.get(BOT_SECTION, 'plex_pass')
|
||||
PLEX_SERVER_NAME = config.get(BOT_SECTION, 'plex_server_name')
|
||||
except:
|
||||
print("Could not load plex config")
|
||||
plex_configured = False
|
||||
|
||||
# Get Plex roles config
|
||||
@@ -91,9 +91,12 @@ except:
|
||||
USE_PLEX = False
|
||||
|
||||
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:
|
||||
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:
|
||||
try:
|
||||
@@ -120,9 +123,11 @@ class app(commands.Cog):
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
print('Made by Yoruio https://github.com/Yoruio/')
|
||||
print('Forked from Invitarr https://github.com/Sleepingpirates/Invitarr')
|
||||
print('Named by lordfransie')
|
||||
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('------')
|
||||
|
||||
@@ -299,7 +304,7 @@ class app(commands.Cog):
|
||||
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_SERVER_URL} to log in!")
|
||||
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
|
||||
|
||||
@@ -12,7 +12,7 @@ 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', '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
|
||||
Discord_bot_token = ""
|
||||
@@ -96,6 +96,14 @@ except:
|
||||
print("Could not load Jellyfin config")
|
||||
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
|
||||
try:
|
||||
jellyfin_roles = config.get(BOT_SECTION, 'jellyfin_roles')
|
||||
@@ -133,12 +141,6 @@ except:
|
||||
print("Could not get Plex enable config. Defaulting to 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():
|
||||
"""
|
||||
Function to return current config
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import sqlite3
|
||||
|
||||
from app.bot.helper.dbupdater import check_table_version, update_table
|
||||
|
||||
DB_URL = 'app/config/app.db'
|
||||
DB_TABLE = 'clients'
|
||||
|
||||
def create_connection(db_file):
|
||||
""" create a database connection to a SQLite database """
|
||||
@@ -26,7 +29,7 @@ def checkTableExists(dbcon, tablename):
|
||||
conn = create_connection(DB_URL)
|
||||
|
||||
# Checking if table exists
|
||||
if checkTableExists(conn, 'clients'):
|
||||
if checkTableExists(conn, DB_TABLE):
|
||||
print('Table exists.')
|
||||
else:
|
||||
conn.execute(
|
||||
@@ -38,6 +41,8 @@ else:
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);''')
|
||||
|
||||
update_table(conn, DB_TABLE)
|
||||
|
||||
def save_user_email(username, email):
|
||||
if username and email:
|
||||
conn.execute(f"""
|
||||
|
||||
65
app/bot/helper/dbupdater.py
Normal file
65
app/bot/helper/dbupdater.py
Normal 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('------')
|
||||
|
||||
@@ -164,12 +164,12 @@ 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)
|
||||
response = requests.request("GET", url, params=querystring, timeout=5)
|
||||
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)
|
||||
response = requests.request("GET", url, params=querystring, timeout=5)
|
||||
return response.status_code
|
||||
@@ -18,11 +18,11 @@ async def embedcustom(recipient, title, fields, ephemeral=True):
|
||||
async def send_info(recipient, message, ephemeral=True):
|
||||
if isinstance(recipient, discord.InteractionResponse):
|
||||
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)
|
||||
|
||||
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)
|
||||
elif isinstance(recipient, discord.InteractionResponse):
|
||||
await recipient.send_message(embed=embed, ephemeral = ephemeral)
|
||||
14
app/bot/helper/textformat.py
Normal file
14
app/bot/helper/textformat.py
Normal 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
27
run.py
@@ -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.jellyfinhelper as jelly
|
||||
from app.bot.helper.message import *
|
||||
from requests import ConnectTimeout
|
||||
|
||||
maxroles = 10
|
||||
|
||||
if switch == 0:
|
||||
print("Missing Config.")
|
||||
sys.exit()
|
||||
|
||||
print(f"V {MEMBARR_VERSION}")
|
||||
|
||||
class Bot(commands.Bot):
|
||||
def __init__(self) -> None:
|
||||
print("Initializing Discord bot")
|
||||
@@ -154,7 +154,8 @@ async def jellyrolels(interaction: discord.Interaction):
|
||||
|
||||
@jellyfin_commands.command(name="setup", description="Setup Jellyfin integration")
|
||||
@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
|
||||
server_url = server_url.rstrip('/')
|
||||
|
||||
@@ -164,28 +165,36 @@ async def setupjelly(interaction: discord.Interaction, server_url: str, api_key:
|
||||
pass
|
||||
elif server_status == 401:
|
||||
# Unauthorized
|
||||
await embederror(interaction.response, "API key provided is invalid")
|
||||
await embederror(interaction.followup, "API key provided is invalid")
|
||||
return
|
||||
elif server_status == 403:
|
||||
# Forbidden
|
||||
await embederror(interaction.response, "API key provided does not have permissions")
|
||||
await embederror(interaction.followup, "API key provided does not have permissions")
|
||||
return
|
||||
elif server_status == 404:
|
||||
# page not found
|
||||
await embederror(interaction.response, "Server endpoint provided was not found")
|
||||
await embederror(interaction.followup, "Server endpoint provided was not found")
|
||||
return
|
||||
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:
|
||||
print("Exception while testing Jellyfin connection")
|
||||
print(type(e).__name__)
|
||||
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
|
||||
|
||||
confighelper.change_config("jellyfin_server_url", str(server_url))
|
||||
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.")
|
||||
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()
|
||||
print("Bot has been restarted. Give it a few seconds.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user