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.
|
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.**
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"""
|
||||||
|
|||||||
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"
|
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
|
||||||
@@ -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)
|
||||||
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.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.")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user