diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9918e0d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM tiangolo/uwsgi-nginx-flask:python3.8-alpine +RUN apk update && apk add gcc libc-dev make git libffi-dev openssl-dev python3-dev libxml2-dev libxslt-dev +ENV LISTEN_PORT 5001 +EXPOSE 5001 +COPY ./app /app +RUN pip install -r requirements.txt && python3 setup.py +ENV STATIC_PATH /app/app/static diff --git a/app/app/__init__.py b/app/app/__init__.py new file mode 100644 index 0000000..82a6914 --- /dev/null +++ b/app/app/__init__.py @@ -0,0 +1,19 @@ +import os +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_bcrypt import Bcrypt +from flask_login import LoginManager + +app = Flask(__name__) + +SECRET_KEY = os.urandom(32) +app.config['SECRET_KEY'] = SECRET_KEY +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///bot/app.db' + +db = SQLAlchemy(app) +bcrypt = Bcrypt(app) +login_manager = LoginManager(app) +login_manager.login_view = 'login' +login_manager.login_message_category = 'info' + +from app import routes diff --git a/app/app/bot/Invitarr.py b/app/app/bot/Invitarr.py new file mode 100644 index 0000000..9f38538 --- /dev/null +++ b/app/app/bot/Invitarr.py @@ -0,0 +1,194 @@ +#Copyright 2020 Sleepingpirate. +import os +from os import environ +import discord +from discord.ext import commands +import asyncio +from plexapi.myplex import MyPlexAccount +from discord import Webhook, AsyncWebhookAdapter +import aiohttp +from dotenv import load_dotenv +import configparser +CONFIG_PATH = 'app/config/config.ini' +BOT_SECTION = 'bot_envs' + +try: + config = configparser.ConfigParser() + config.read(CONFIG_PATH) +except: + print("Cannot find config") + +# settings +Discord_bot_token = config.get(BOT_SECTION, 'discord_bot_token') +roleid = int(config.get(BOT_SECTION, 'role_id')) +PLEXUSER = config.get(BOT_SECTION, 'plex_user') +PLEXPASS = config.get(BOT_SECTION, 'plex_pass') +PLEX_SERVER_NAME = config.get(BOT_SECTION, 'plex_server_name') +Plex_LIBS = config.get(BOT_SECTION, 'plex_libs') +chan = int(config.get(BOT_SECTION, 'channel_id')) +ownerid = int(config.get(BOT_SECTION, 'owner_id')) +auto_remove_user = config.get(BOT_SECTION, 'auto_remove_user') if config.get(BOT_SECTION, 'auto_remove_user') else False + +li = list(Plex_LIBS.split(',')) +Plex_LIBS = li + +#rolenames = list(roleid.split(',')) + +if auto_remove_user: + print("auto remove user = True") + import db as db + +account = MyPlexAccount(PLEXUSER, PLEXPASS) +plex = account.resource(PLEX_SERVER_NAME).connect() # returns a PlexServer instance + +def plexadd(plexname): + try: + plex.myPlexAccount().inviteFriend(user=plexname, server=plex, sections=Plex_LIBS, allowSync=False, + allowCameraUpload=False, allowChannels=False, filterMovies=None, + filterTelevision=None, filterMusic=None) + + except Exception as e: + print(e) + return False + else: + print(plexname +' has been added to plex (☞ຈل͜ຈ)☞') + return True + + +def plexremove(plexname): + try: + plex.myPlexAccount().removeFriend(user=plexname) + except Exception as e: + print(e) + return False + else: + print(plexname +' has been removed from plex (☞ຈل͜ຈ)☞') + return True + +class MyClient(discord.Client): + async def on_ready(self): + print('Made by Sleepingpirate https://github.com/Sleepingpirates/') + print('Logged in as') + print(self.user.name) + print(self.user.id) + print('------') + await client.change_presence(activity=discord.Game(name="Do -help in {}".format(client.get_channel(chan)))) + + async def on_member_update(self, before, after): + role = after.guild.get_role(roleid) + secure = client.get_channel(chan) + if (role in after.roles and role not in before.roles): + await after.send('Welcome To '+ PLEX_SERVER_NAME +'. Just reply with your email so we can add you to Plex!') + await after.send('I will wait 10 minutes for your message, if you do not send it by then I will cancel the command.') + def check(m): + return m.author == after and not m.guild + try: + email = await client.wait_for('message', timeout=600, check=check) + except asyncio.TimeoutError: + await after.send('Timed Out. Message Server Admin So They Can Add You Manually.') + else: + await asyncio.sleep(5) + await after.send('Got it we will be processing your email shortly') + print(email.content) #make it go to a log channel + plexname = str(email.content) + if plexadd(plexname): + if auto_remove_user: + db.save_user(str(after.id), email.content) + await asyncio.sleep(20) + await after.send('You have Been Added To Plex!') + await secure.send(plexname + ' ' + after.mention + ' was added to plex') + else: + await after.send('There was an error adding this email address. Message Server Admin.') + elif(role not in after.roles and role in before.roles): + if auto_remove_user: + try: + user_id = after.id + email = db.get_useremail(user_id) + plexremove(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: + print("Cannot remove this user from plex.") + + + async def on_message(self, message): + secure = client.get_channel(chan) + if message.author.id == self.user.id: + return + + if message.author.id == ownerid: + if message.content.startswith('-db add'): + mgs = message.content.replace('-db add ','') + try: + mgs = mgs.split(' ') + email = mgs[0] + bad_chars = ['<','>','@','!'] + user_id = mgs[1] + for i in bad_chars: + user_id = user_id.replace(i, '') + db.save_user(user_id, email) + await secure.send(email + ' ' + mgs[1] + ' was added to plex') + except: + await message.channel.send('Cannot add this user to db.') + print("Cannot add this user to db.") + await message.delete() + + if str(message.channel) == str(secure): + if message.content.startswith('-db ls') or message.content.startswith('-db rm'): + embed = discord.Embed(title='Invitarr Database.') + all = db.read_useremail() + for index, peoples in enumerate(all): + index = index + 1 + id = int(peoples[1]) + dbuser = client.get_user(id) + dbemail = peoples[2] + embed.add_field(name=f"**{index}. {dbuser.name}**", value=dbemail+'\n', inline=False) + if message.content.startswith('-db ls'): + await secure.send(embed = embed) + else: + try: + position = message.content.replace("-db rm", "") + position = int(position) - 1 + id = all[position][1] + email = db.get_useremail(id) + deleted = db.delete_user(id) + if deleted: + print("Removed {} from db".format(email)) + await secure.send("Removed {} from db".format(email)) + else: + print("Cannot remove this user from db.") + except Exception as e: + print(e) + + if message.content.startswith('-help'): + embed = discord.Embed(title='Invitarr Bot Commands', description='Made by [Sleepingpirates](https://github.com/Sleepingpirates/Invitarr), [Join Discord Server](https://discord.gg/vcxCytN)') + embed.add_field(name='-plexadd ', value='This command is used to add an email to plex', inline=False) + embed.add_field(name='-plexrm ', value='This command is used to remove an email from plex', inline=False) + embed.add_field(name='-db ls', value='This command is used list Invitarrs database', inline=False) + embed.add_field(name='-db add <@user>', value='This command is used add exsisting users email and discord id to the DB.', inline=False) + embed.add_field(name='-db rm ', value='This command is used remove a record from the Db. Use -db ls to determine record position. ex: -db rm 1', inline=False) + await secure.send(embed = embed) + + + async def on_member_remove(self, member): + if auto_remove_user: + try: + user_id = member.id ## not there + email = db.get_useremail(user_id) + plexremove(email) + deleted = db.delete_user(user_id) + if deleted: + print("Removed {} from db".format(email)) + secure = client.get_channel(chan) + await secure.send(email + ' ' + member.mention + 'was removed from plex because they left the server') + else: + print("Cannot remove this user from db.") + except: + print("Cannot remove this user from plex.") + +client = MyClient() +client.run(Discord_bot_token) diff --git a/app/app/bot/db.py b/app/app/bot/db.py new file mode 100644 index 0000000..b982f0b --- /dev/null +++ b/app/app/bot/db.py @@ -0,0 +1,83 @@ +import sqlite3 + +DB_URL = 'app/config/app.db' + +def create_connection(db_file): + """ create a database connection to a SQLite database """ + conn = None + try: + conn = sqlite3.connect(db_file) + print("Connected to db") + except Error as e: + print("error in connecting to db") + finally: + if conn: + return conn + +def checkTableExists(dbcon, tablename): + dbcur = dbcon.cursor() + dbcur.execute("""SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='{0}';""".format(tablename.replace('\'', '\'\''))) + if dbcur.fetchone()[0] == 1: + dbcur.close() + return True + dbcur.close() + return False + +conn = create_connection(DB_URL) + +# Checking if table exists +if checkTableExists(conn, 'clients'): + print('Table exists.') +else: + conn.execute(''' + CREATE TABLE "clients" ( + "id" INTEGER NOT NULL UNIQUE, + "discord_username" TEXT NOT NULL UNIQUE, + "email" TEXT NOT NULL, + PRIMARY KEY("id" AUTOINCREMENT) + ); + ''') + +def save_user(username, email): + if username and email: + conn.execute("INSERT INTO clients (discord_username, email) VALUES ('"+ username +"', '" + email + "')"); + conn.commit() + print("User added to db.") + else: + return "Username or email cannot be empty" + +def get_useremail(username): + if username: + try: + cursor = conn.execute('SELECT discord_username, email from clients where discord_username="{}";'.format(username)) + for row in cursor: + email = row[1] + if email: + return email + else: + return "No users found" + except: + return "error in fetching from db" + else: + return "username cannot be empty" + +def delete_user(username): + if username: + try: + conn.execute('DELETE from clients where discord_username="{}";'.format(username)) + conn.commit() + return True + except: + return False + else: + return "username cannot be empty" + +def read_useremail(): + cur = conn.cursor() + cur.execute("SELECT * FROM clients") + rows = cur.fetchall() + all = [] + for row in rows: + #print(row[1]+' '+row[2]) + all.append(row) + return all diff --git a/app/app/configHandler.py b/app/app/configHandler.py new file mode 100644 index 0000000..ede8fc6 --- /dev/null +++ b/app/app/configHandler.py @@ -0,0 +1,56 @@ +import configparser + +config = configparser.ConfigParser() + +CONFIG_PATH = 'app/config/config.ini' +BOT_SECTION = 'bot_envs' +CONFIG_KEYS = ['username', 'password', 'discord_bot_token', 'plex_user', 'plex_pass', + 'role_id', 'plex_server_name', 'plex_libs', 'owner_id', 'channel_id', + 'auto_remove_user'] + +def get_config(): + """ + Function to return current config + """ + try: + config.read(CONFIG_PATH) + return config + except Exception as e: + print(e) + print('error in reading config') + return None + + +def change_config(key, value): + """ + Function to change the key, value pair in config + """ + try: + config = configparser.ConfigParser() + config.read(CONFIG_PATH) + except Exception as e: + print(e) + print("Cannot Read config.") + + try: + config.set(BOT_SECTION, key, str(value)) + except Exception as e: + config.add_section(BOT_SECTION) + config.set(BOT_SECTION, key, str(value)) + + try: + with open(CONFIG_PATH, 'w') as configfile: + config.write(configfile) + except Exception as e: + print(e) + print("Cannot write to config.") + +def change_config_form(form_output): + """ + Function to change config on the basis of form_output from web + """ + for key in CONFIG_KEYS: + try: + change_config(key, form_output[key].data) + except: + pass diff --git a/app/app/forms.py b/app/app/forms.py new file mode 100644 index 0000000..047dfa9 --- /dev/null +++ b/app/app/forms.py @@ -0,0 +1,52 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, SubmitField, BooleanField, IntegerField +from wtforms.validators import DataRequired + +class LoginForm(FlaskForm): + + username = StringField('Username', + validators=[DataRequired()]) + + password = PasswordField('Password', + validators=[DataRequired()]) + + submit = SubmitField('Save Changes') + +class GeneralForm(FlaskForm): + + username = StringField('Username', + validators=[DataRequired()]) + + password = PasswordField('Password', + validators=[DataRequired()]) + + submit = SubmitField('Save Changes') + +class BotForm(FlaskForm): + + discord_bot_token = StringField('Discord Bot Token', + validators=[DataRequired()]) + + role_id = IntegerField('Role Id', + validators=[DataRequired()]) + + channel_id = IntegerField('Channel Id', + validators=[DataRequired()]) + owner_id = IntegerField('Owner Id', + validators=[DataRequired()]) + # Auto Remove User + auto_remove_user = BooleanField('On') + + submit = SubmitField('Save Changes') + +class PlexForm(FlaskForm): + + plex_user = StringField('Plex User', + validators=[DataRequired()]) + plex_pass = StringField('Plex Pass', + validators=[DataRequired()]) + plex_server_name = StringField('Plex Server Name', + validators=[DataRequired()]) + plex_libs = StringField('Plex Libs', + validators=[DataRequired()]) + submit = SubmitField('Save Changes') \ No newline at end of file diff --git a/app/app/models.py b/app/app/models.py new file mode 100644 index 0000000..ea10fbd --- /dev/null +++ b/app/app/models.py @@ -0,0 +1,15 @@ +from app import db, login_manager +from flask_login import UserMixin + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + + +class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(20), unique=True, nullable=False) + password = db.Column(db.String(60), nullable=False) + + def __repr__(self): + return f"User('{self.username}')" diff --git a/app/app/routes.py b/app/app/routes.py new file mode 100644 index 0000000..bf1dead --- /dev/null +++ b/app/app/routes.py @@ -0,0 +1,120 @@ +from flask import render_template, flash, request, redirect, url_for +from flask_login import login_user, current_user, logout_user, login_required +import subprocess + +from app import app, db, bcrypt +from app.models import User +from app.forms import LoginForm, GeneralForm, BotForm, PlexForm +from app import configHandler + +db.create_all() +BOT_SECTION = 'bot_envs' +proc = None + +def manage_bot(option): + global proc + if option == 'start': + proc = subprocess.Popen(["python", "app/bot/Invitarr.py"]) + elif option == 'kill': + if proc: + proc.terminate() + +try: + manage_bot('start') +except: + print("Some error in starting bot. Please check the config vars") + +@app.route("/login", methods=['GET', 'POST']) +def login(): + if current_user.is_authenticated: + return redirect(url_for('home')) + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user and bcrypt.check_password_hash(user.password, form.password.data): + login_user(user, remember=True) + next_page = request.args.get('next') + return redirect(next_page) if next_page else redirect(url_for('home')) + else: + flash('Login Unsuccessful. Please check email and password') + return render_template('login.html', title='Login', form=form) + +@app.route('/logout') +@login_required +def logout(): + flash('Logged out.') + logout_user() + return redirect(url_for('login')) + +@app.route('/', methods=['GET', 'POST']) +@login_required +def home(): + form = GeneralForm() + if request.method == 'GET': + user = User.query.all()[0] + form.username.data = user.username + form.password.data = user.password + elif request.method == 'POST': + if form.validate_on_submit(): + user = User.query.all()[0] + user.username = form.username.data + hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') + user.password = hashed_password + db.session.commit() + logout_user() + flash('Details updated.') + return redirect(url_for('login')) + return render_template('index.html', form=form) + +@app.route('/bot', methods=['GET', 'POST']) +@login_required +def bot(): + form = BotForm() + if request.method == 'GET': + try: + config = configHandler.get_config() + form.discord_bot_token.data = config.get(BOT_SECTION, 'discord_bot_token') + form.role_id.data = config.get(BOT_SECTION, 'role_id') + form.channel_id.data = config.get(BOT_SECTION, 'channel_id') + form.owner_id.data = config.get(BOT_SECTION, 'owner_id') + form.auto_remove_user.data = config.get(BOT_SECTION, 'auto_remove_user') + except: + pass + elif request.method == 'POST': + if form.validate_on_submit(): + try: + configHandler.change_config_form(form) + flash('Settings updated.') + manage_bot('kill') + manage_bot('start') + except: + flash('Some error in updating settings') + return render_template('bot.html', form = form) + +@app.route('/plex', methods=['GET', 'POST']) +@login_required +def plex(): + form = PlexForm() + if request.method == 'GET': + try: + config = configHandler.get_config() + form.plex_user.data = config.get(BOT_SECTION, 'plex_user') + form.plex_pass.data = config.get(BOT_SECTION, 'plex_pass') + form.plex_server_name.data = config.get(BOT_SECTION, 'plex_server_name') + form.plex_libs.data = config.get(BOT_SECTION, 'plex_libs') + except: + pass + elif request.method == 'POST': + if form.validate_on_submit(): + try: + configHandler.change_config_form(form) + flash('Settings updated.') + except: + flash('Some error in updating settings') + try: + manage_bot('kill') + manage_bot('start') + except Exception as e: + raise Exception(e) + + return render_template('plex.html', form = form) diff --git a/app/app/static/favicon.ico b/app/app/static/favicon.ico new file mode 100644 index 0000000..e9fb830 Binary files /dev/null and b/app/app/static/favicon.ico differ diff --git a/app/app/static/img/logo.svg b/app/app/static/img/logo.svg new file mode 100644 index 0000000..64ff58c --- /dev/null +++ b/app/app/static/img/logo.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/app/static/main.css b/app/app/static/main.css new file mode 100644 index 0000000..3a5495e --- /dev/null +++ b/app/app/static/main.css @@ -0,0 +1,86 @@ +/* Universal */ +:root { + --main-bg-color: #000000; + --content-background: #17151C; + --secondary-content-background: #2A2731; + --primary-text-color: #FFFFFF; +} + +body{ + background-color: var(--main-bg-color); + color: var(--primary-text-color); +} + +/* Header */ +h3{ + font-weight: bold; + display: inline-block; + margin: 0.5em 0 1.5em 0.25em; + padding-top: 0.5em; +} + +.header img{ + vertical-align: middle; +} + +a.nav-link{ + color: var(--primary-text-color); +} + +.header button{ + max-height: 50px; + /* margin: 1em 0.5em; */ +} + +/* Form */ +.form-container{ + padding: 1.5em; + padding-top: 2em; + border-radius: 0 0 10px 10px; + background-color: var(--content-background); +} + +.form-stringfield{ + background-color: var(--secondary-content-background); + color: var(--primary-text-color); +} + +.form-stringfield:focus{ + background-color: var(--secondary-content-background); + color: var(--primary-text-color); +} + +.custom-checkbox{ + padding-left: 3em; +} + +/* Modal */ +.modal-content{ + background-color: var(--secondary-content-background); + padding: 1em !important; +} + +.modal-body{ + text-align: center; +} + +.modal-header{ + padding: 1em; + border-bottom: none; +} + +.modal-header h4{ + color: var(--primary-text-color) +} + +/* Login Page */ +.login{ + margin: auto; + margin-top: 5em; + text-align: center; +} + +.login h2{ + margin-top: 1em; + font-weight: bold; +} diff --git a/app/app/templates/bot.html b/app/app/templates/bot.html new file mode 100644 index 0000000..7e0f399 --- /dev/null +++ b/app/app/templates/bot.html @@ -0,0 +1,194 @@ +{%extends 'layout.html'%} +{% block content %} +
+ +

Invitarr Configuration

+ +
+ +
+ +
+ {{ form.hidden_tag() }} +
+
+
+ {{ form.discord_bot_token.label(class="form-control-label")}} +
+
+
+ {% if form.discord_bot_token.errors %} + {{ form.discord_bot_token(class="form-control form-stringfield")}} + Some text describing this field +
+ {% for error in form.discord_bot_token.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.discord_bot_token(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.role_id.label(class="form-control-label")}} +
+
+
+ {% if form.role_id.errors %} + {{ form.role_id(class="form-control form-stringfield")}} +
+ {% for error in form.role_id.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.role_id(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.channel_id.label(class="form-control-label")}} +
+
+
+ {% if form.channel_id.errors %} + {{ form.channel_id(class="form-control form-stringfield")}} +
+ {% for error in form.channel_id.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.channel_id(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.owner_id.label(class="form-control-label")}} +
+
+
+ {% if form.owner_id.errors %} + {{ form.owner_id(class="form-control form-stringfield")}} +
+ {% for error in form.owner_id.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.owner_id(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+

Auto Remove User

+
+
+
+ {% if form.auto_remove_user.errors %} + {{ form.auto_remove_user(class="custom-control-input form-stringfield", id="removeSwitch")}} + {{ form.auto_remove_user.label(class="custom-control-label", for="removeSwitch")}} +
+ {% for error in form.auto_remove_user.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.auto_remove_user(class="custom-control-input form-stringfield", id="removeSwitch")}} + {{ form.auto_remove_user.label(class="custom-control-label", for="removeSwitch")}} + {% endif %} +
+
+
+
+ {{ form.submit(class="btn btn-primary submit-btn") }} +
+
+
+ +{% endblock content %} + +{% block script %} + +{% endblock script %} diff --git a/app/app/templates/index.html b/app/app/templates/index.html new file mode 100644 index 0000000..bc0f08b --- /dev/null +++ b/app/app/templates/index.html @@ -0,0 +1,131 @@ +{%extends 'layout.html'%} +{% block content %} +
+ +

Invitarr Configuration

+
+ +
+ +
+ {{ form.hidden_tag() }} +
+
+
+ {{ form.username.label(class="form-control-label")}} +
+
+
+ {% if form.username.errors %} + {{ form.username(class="form-control form-stringfield")}} + Some text describing this field +
+ {% for error in form.username.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.username(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.password.label(class="form-control-label")}} +
+
+
+ {% if form.password.errors %} + {{ form.password(class="form-control form-stringfield")}} +
+ {% for error in form.password.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.password(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+ {{ form.submit(class="btn btn-primary submit-btn") }} +
+
+
+ +{% endblock content %} + +{% block script %} + +{% endblock script %} diff --git a/app/app/templates/layout.html b/app/app/templates/layout.html new file mode 100644 index 0000000..2b3982d --- /dev/null +++ b/app/app/templates/layout.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + {% if title %} + {{ title }} - Invitarr + {% else %} + Invitarr + {% endif %} + + +
+ {% block content %}{% endblock %} +
+ + + + + + + {% block script %}{% endblock %} + + + + diff --git a/app/app/templates/login.html b/app/app/templates/login.html new file mode 100644 index 0000000..fb8576e --- /dev/null +++ b/app/app/templates/login.html @@ -0,0 +1,105 @@ +{%extends 'layout.html'%} +{% block content %} + + +{% endblock content %} + + +{% block script %} + +{% endblock script %} \ No newline at end of file diff --git a/app/app/templates/plex.html b/app/app/templates/plex.html new file mode 100644 index 0000000..91d23f9 --- /dev/null +++ b/app/app/templates/plex.html @@ -0,0 +1,155 @@ +{%extends 'layout.html'%} +{% block content %} +
+ +

Invitarr Configuration

+
+ +
+ +
+ {{ form.hidden_tag() }} +
+
+
+ {{ form.plex_user.label(class="form-control-label")}} +
+
+
+ {% if form.plex_user.errors %} + {{ form.plex_user(class="form-control form-stringfield")}} + Some text describing this field +
+ {% for error in form.plex_user.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.plex_user(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.plex_pass.label(class="form-control-label")}} +
+
+
+ {% if form.plex_pass.errors %} + {{ form.plex_pass(class="form-control form-stringfield")}} +
+ {% for error in form.plex_pass.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.plex_pass(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.plex_server_name.label(class="form-control-label")}} +
+
+
+ {% if form.plex_server_name.errors %} + {{ form.plex_server_name(class="form-control form-stringfield")}} +
+ {% for error in form.plex_server_name.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.plex_server_name(class="form-control form-stringfield")}} + {% endif %} +
+
+
+
+
+
+ {{ form.plex_libs.label(class="form-control-label")}} +
+
+
+ {% if form.plex_libs.errors %} + {{ form.plex_libs(class="form-control form-stringfield")}} +
+ {% for error in form.plex_libs.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.plex_libs(class="form-control form-stringfield")}} + {% endif %} + Enter comma seperated values +
+
+
+
+ {{ form.submit(class="btn btn-primary submit-btn") }} +
+
+
+ +{% endblock content %} + +{% block script %} + +{% endblock script %} diff --git a/app/requirements.txt b/app/requirements.txt new file mode 100644 index 0000000..2c13823 --- /dev/null +++ b/app/requirements.txt @@ -0,0 +1,33 @@ +aiohttp==3.6.2 +async-timeout==3.0.1 +attrs==19.3.0 +bcrypt==3.1.7 +certifi==2020.4.5.2 +cffi==1.14.0 +chardet==3.0.4 +click==7.1.2 +discord.py==1.3.3 +Flask==1.1.2 +Flask-Bcrypt==0.7.1 +Flask-Login==0.5.0 +Flask-SQLAlchemy==2.4.3 +Flask-WTF==0.14.3 +idna==2.9 +idna-ssl==1.1.0 +itsdangerous==1.1.0 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +multidict==4.7.6 +plex.py==0.9.0 +PlexAPI==4.0.0 +pycparser==2.20 +python-dotenv==0.13.0 +requests==2.23.0 +six==1.15.0 +SQLAlchemy==1.3.17 +typing-extensions==3.7.4.2 +urllib3==1.25.9 +websockets==8.1 +Werkzeug==1.0.1 +WTForms==2.3.1 +yarl==1.4.2 diff --git a/app/run.py b/app/run.py new file mode 100644 index 0000000..b40771f --- /dev/null +++ b/app/run.py @@ -0,0 +1,17 @@ +from app import app +from app import db, bcrypt +from app.models import User +from os import path +''' +if path.exists("app.config.app.db") is False: + db.create_all() + try: + hashed_password = bcrypt.generate_password_hash(DEFAULT_PASS).decode('utf-8') + user = User(username=DEFAULT_USER, password=hashed_password) + db.session.add(user) + db.session.commit() + except: + print("Some error in setting up.") +''' +if __name__ == "__main__": + app.run(debug=True) diff --git a/app/setup.py b/app/setup.py new file mode 100644 index 0000000..8707b15 --- /dev/null +++ b/app/setup.py @@ -0,0 +1,14 @@ +from app import db, bcrypt +from app.models import User + +DEFAULT_USER = "admin" +DEFAULT_PASS = "admin" +db.create_all() + +try: + hashed_password = bcrypt.generate_password_hash(DEFAULT_PASS).decode('utf-8') + user = User(username=DEFAULT_USER, password=hashed_password) + db.session.add(user) + db.session.commit() +except: + print("Some error in setting up.") diff --git a/app/uwsgi.ini b/app/uwsgi.ini new file mode 100644 index 0000000..c4a31cb --- /dev/null +++ b/app/uwsgi.ini @@ -0,0 +1,4 @@ +[uwsgi] +module = run +callable = app +enable-threads = true