├── .gitignore
├── LICENSE
├── Mazi.code-workspace
├── README.md
├── assets
├── catart.png
├── mazilogodark.png
├── mazilogolight.png
└── mazilogosquare.png
├── bot.py
├── cogs
├── counts.py
├── donate.py
├── eggotyou.py
├── faq.py
├── help.py
├── host.py
├── join.py
├── link.py
├── movies.py
├── ping.py
├── project.py
├── search.py
└── website.py
├── dockerfile
├── requirements.txt
├── static
├── favicon.ico
└── styles.css
└── templates
├── failpage.html
└── statuspage.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 | creds.db
131 | USEFUL.txt
132 | modules/**
133 | resources/**
134 | frontend/**
135 | backend/**
136 | creds.json
137 | .env
138 | .dockerignore
139 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 David
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Mazi.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ],
7 | "settings": {
8 | "dotenv.enableAutocloaking": false
9 | }
10 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mazi
2 | A discord bot to watch plex with your friends easily and through discord. The first ever Movie Night Discord Bot.
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | #
15 |
16 |
17 |

18 |

19 |
20 |
21 | # Invite = [Invite Mazi to your server](https://discord.com/api/oauth2/authorize?client_id=978163786886311977&permissions=93184&redirect_uri=https%3A%2F%2Fapi.mazi.pw%2Fdiscordsuccess&scope=bot%20applications.commands)
22 | If you have any problems please read the [FAQ](https://github.com/Wamy-Dev/Mazi/wiki/FAQ) first, then message me on Discord using the server invite below if you still need help. The bot is also on [Top.gg](https://top.gg/bot/978163786886311977?s=0c29b1e62673d)! Please go give it a vote!
23 |
24 | # Website = [Mazi.pw](https://mazi.pw)
25 |
26 | Here you can link your accounts so you can use the Mazi Bot. Accounts are required to use the Mazi bot. You may get an alert from Plex when linking about a "security alert", and it will show the server IP, this will be fixed eventually.
27 |
28 | # How does this work?
29 | 1. First sign up for an account at [Mazi.pw](https://mazi.pw/user).
30 | 2. Link both your Discord and Plex accounts to it. If you are hosting Watch Together Sessions, make sure to add your public server url. This can be an ip or a url to your server. Make sure it is valid before submitting!
31 | 3. If you are hosting, use `/host` to begin a session. It will make a session, and will wait 5 minutes for people to join the room. If you are joining, use `/join` to join a joinable room. Make sure you are in the same channel as where the room was started.
32 | 4. After the 5 minutes has passed, the room will close and requests will go out to users who are not already friends with the hoster on Plex with a grace period of 5 minutes. After those 5 minutes, all users who have joined need to now open up Plex on any device, and join the Watch Together session.
33 | 5. Get your popcorn and drinks ready, cause the movie is starting!
34 |
35 | # Do you store my data?
36 | Yes, in an effort to be as transparent as possible, this is all the data we store. `Discord ID, Discord Email, Plex Auth Token (encrypted securely in AES), Plex email, Plex ID, Plex UUID, Plex Username, Plex Server URL, Plex User Profile Image, and your Mazi Email`. This data is stored securely in [Firebase Firestore](https://firebase.google.com/support/privacy) and I cannot decrypt your Plex Auth Token, or access either of your Discord or Plex accounts in any way. If you would like to delete all of your data, there is an "Unlink Data" button in the bottom left of the user panel. For more please read the [Mazi Privacy Policy](https://github.com/Wamy-Dev/Mazi/wiki/Privacy-Policy) for more information.
37 |
38 | # Features
39 | - [x] Easy Plex and Discord Linking
40 | - [x] Beautiful
41 | - [X] Ease of Use
42 | - [X] Fast
43 |
44 | #
45 |
46 | ##### This was made by me, Wamy.
47 | ##### Join my Discord server: [Here](https://discord.gg/47SnjxgBFb)
48 | ##### If you want to contact me: [My personal contact page](https://homeonacloud.com/contact)
49 | ##### If you want to donate to show your thanks: [My personal donation page](https://homeonacloud.com/donate)
50 | ##### This project was made with community in mind, please be nice and don't steal my work.
51 |
--------------------------------------------------------------------------------
/assets/catart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wamy-Dev/Mazi/5ccff717bab1b1db16e239e51c3de896053e067d/assets/catart.png
--------------------------------------------------------------------------------
/assets/mazilogodark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wamy-Dev/Mazi/5ccff717bab1b1db16e239e51c3de896053e067d/assets/mazilogodark.png
--------------------------------------------------------------------------------
/assets/mazilogolight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wamy-Dev/Mazi/5ccff717bab1b1db16e239e51c3de896053e067d/assets/mazilogolight.png
--------------------------------------------------------------------------------
/assets/mazilogosquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wamy-Dev/Mazi/5ccff717bab1b1db16e239e51c3de896053e067d/assets/mazilogosquare.png
--------------------------------------------------------------------------------
/bot.py:
--------------------------------------------------------------------------------
1 | # ______ __ __ __
2 | # / ____/___ _____ _ / / / /_ ______ / /____ ___________
3 | # / __/ / __ `/ __ `/ / /_/ / / / / __ \/ __/ _ \/ ___/ ___(_)
4 | # / /___/ /_/ / /_/ / / __ / /_/ / / / / /_/ __/ / (__ )
5 | #/_____/\__, /\__, / /_/ /_/\__,_/_/ /_/\__/\___/_/ /____(_)
6 | # /____//____/
7 | # directlycrazy.1812
8 | # Mato.5201
9 | # Andr01dx86.1886
10 | # DARTUBE.6145
11 | # Cholo Lord.6202
12 | # cocolaninano#7812
13 | # piny.
14 | # kayliatalon
15 | # yiga_
16 | # cekirge1972
17 | # Matthew Ariel
18 | import requests
19 | import os
20 | import urllib3
21 | import logging
22 | from logging import getLogger
23 | from quart.logging import default_handler
24 | getLogger('quart.serving').removeHandler(default_handler)
25 | getLogger('quart.serving').setLevel(logging.ERROR)
26 | session = requests.Session()
27 | session.verify = False
28 | urllib3.disable_warnings()
29 | import discord
30 | from discord.ext import commands
31 | from quart import Quart, render_template
32 | from decouple import config
33 | import firebase_admin
34 | from firebase_admin import credentials
35 | import asyncio
36 | from threading import Thread
37 | import requests
38 | from datetime import datetime
39 | from humanfriendly import format_timespan
40 | #quart
41 | app = Quart(__name__)
42 | ready = False
43 | inittime = datetime.now()
44 | @app.route("/", methods = ["get"])
45 | async def index():
46 | while ready:
47 | latencies = []
48 | for i in client.latencies:
49 | data = {}
50 | shard = i[0]
51 | latency = round(i[1] * 1000)#in ms
52 | data["shard"] = shard
53 | data["latency"] = latency
54 | uptime = datetime.now() - inittime
55 | secuptime = uptime.total_seconds()
56 | totaluptime = format_timespan(secuptime)
57 | data["uptime"] = totaluptime
58 | latencies.append(data)
59 | members = 0
60 | servers = client.guilds
61 | servercount = {}
62 | for guild in servers:
63 | id = guild.shard_id
64 | try:
65 | count = servercount[id]
66 | except:
67 | count = 0
68 | count += 1
69 | servercount[id] = count
70 | members += guild.member_count
71 | timeutc = str(datetime.utcnow())
72 | timenow = timeutc[:-7]
73 | #get full number of servers
74 | serverstotal = len(servers)
75 | servers = serverstotal
76 | shardcount = client.shard_count
77 | return await render_template("statuspage.html", latencies=latencies, members=members, servercount=servercount, timenow=timenow, serverstotal=serverstotal, shardcount=shardcount)
78 | else:
79 | return await render_template("failpage.html")
80 | #firebase
81 | cred = credentials.Certificate("creds.json")
82 | firebase_admin.initialize_app(cred)
83 | #discord
84 | CLIENTTOKEN = config('CLIENTTOKEN')
85 | intents = discord.Intents.default()
86 | client = commands.AutoShardedBot(command_prefix = '>', intents=intents)
87 | client.remove_command('help')
88 |
89 | @client.event
90 | async def on_ready():
91 | print(f'Bot is ready. Logged in as {client.user}(ID: {client.user.id})')
92 | print(f'Shards: {client.shard_count}')
93 | await client.change_presence(activity = discord.Activity(type = discord.ActivityType.watching, name = "/help"))
94 | global ready
95 | ready = True
96 | await asyncio.sleep(5)
97 | await client.tree.sync()
98 | print("Tree synced")
99 |
100 | # @client.tree.command(name="reload", description="Reloads a cog.")
101 | # async def reload(interaction: discord.Interaction, cog: str = None):
102 | # try:
103 | # await client.reload_extension(f"cogs.{cog}")
104 | # await interaction.response.send_message(f"Reloaded {cog}")
105 | # await client.tree.sync()
106 | # except Exception as e:
107 | # await interaction.response.send_message(f"{e}")
108 |
109 | class async_discord_thread(Thread):
110 | #thanks @FrankWhoee for this code snippet
111 | def __init__(self):
112 | Thread.__init__(self)
113 | self.loop = asyncio.get_event_loop()
114 | self.start()
115 | async def starter(self):
116 | async def load_extensions():
117 | for filename in os.listdir("./cogs"):
118 | if filename.endswith(".py"):
119 | await client.load_extension(f"cogs.{filename[:-3]}")
120 | await load_extensions()
121 | await client.start(CLIENTTOKEN)
122 | def run(self):
123 | self.name = 'Discord.py'
124 | self.loop.create_task(self.starter())
125 | self.loop.run_forever()
126 | discord_thread = async_discord_thread()
127 | app.run(host="0.0.0.0", port="5001")
128 | # async def load_extensions():
129 | # for filename in os.listdir("./cogs"):
130 | # if filename.endswith(".py"):
131 | # await client.load_extension(f"cogs.{filename[:-3]}")
132 | # async def start():
133 | # await load_extensions()
134 | # await client.start(CLIENTTOKEN)
135 | # asyncio.run(start())
136 |
--------------------------------------------------------------------------------
/cogs/counts.py:
--------------------------------------------------------------------------------
1 | from discord.ext import commands
2 | from discord import Interaction
3 | from discord import app_commands
4 | from firebase_admin import firestore
5 |
6 | db = firestore.client()
7 | countsdoc = db.collection(u'counts').document(u'counts')
8 |
9 | class Counts(commands.Cog):
10 |
11 | def __init__(self, client):
12 | self.client = client
13 |
14 | @app_commands.command(name="counts", description="View how many movie nights have been hosted globally.")
15 | async def donate(self, interaction: Interaction):
16 | counts = countsdoc.get()
17 | previouscount = counts.to_dict()
18 | txt = str(f"""```css\nThe bot has hosted movie night {str(previouscount["counts"] - 1)} times.```""")
19 | await interaction.response.send_message(txt)
20 |
21 | async def setup(client):
22 | await client.add_cog(Counts(client))
--------------------------------------------------------------------------------
/cogs/donate.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 |
6 | class Donate(commands.Cog):
7 |
8 | def __init__(self, client):
9 | self.client = client
10 |
11 | @app_commands.command(name="donate", description="Doante to the Mazi project.")
12 | async def donate(self, interaction: Interaction):
13 |
14 | embed = discord.Embed(title = "Donate to the project", colour = discord.Colour.from_rgb(229,160,13))
15 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
16 | embed.add_field(name = '🔗', value='https://homeonacloud.com/donate', inline = False)
17 | await interaction.response.send_message(embed=embed)
18 |
19 | async def setup(client):
20 | await client.add_cog(Donate(client))
--------------------------------------------------------------------------------
/cogs/eggotyou.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 |
6 | class Modal(discord.ui.Modal, title='You got me!'):
7 | feedback = discord.ui.TextInput(
8 | label='You found the Easter Egg!',
9 | style=discord.TextStyle.long,
10 | default='You found the Easter Egg! If you want your name in the source code, send a screenshot of this to Wamy#0002 and your name will be added.',
11 | required=False,
12 | max_length=135,
13 | )
14 | async def on_submit(self, interaction: discord.Interaction):
15 | await interaction.response.send_message('Thank you for using Mazi.', ephemeral=True)
16 |
17 | class Eggotyou(commands.Cog):
18 |
19 | def __init__(self, client):
20 | self.client = client
21 |
22 | @app_commands.command(name="egg", extras={"hidden": True})
23 | async def egg(self, interaction: Interaction):
24 | await interaction.response.send_modal(Modal())
25 |
26 | async def setup(client):
27 | await client.add_cog(Eggotyou(client))
--------------------------------------------------------------------------------
/cogs/faq.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 |
6 | class Faq(commands.Cog):
7 |
8 | def __init__(self, client):
9 | self.client = client
10 |
11 | @app_commands.command(name="faq", description="Read the Mazi F.A.Q.")
12 | async def faq(self, interaction: Interaction):
13 |
14 | embed = discord.Embed(title = "Mazi F.A.Q.", colour = discord.Colour.from_rgb(229,160,13))
15 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
16 | embed.add_field(name = '🔗', value='https://github.com/Wamy-Dev/Mazi/wiki/FAQ', inline = False)
17 | await interaction.response.send_message(embed=embed)
18 |
19 | async def setup(client):
20 | await client.add_cog(Faq(client))
--------------------------------------------------------------------------------
/cogs/help.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 | import random
6 |
7 | class Help(commands.Cog):
8 |
9 | def __init__(self, client):
10 | self.client = client
11 |
12 | @app_commands.command(name="help", description="Shows the help dialogue.")
13 | async def help(self, interaction: Interaction):
14 | list = ["Now with 100% more help!", "Now with slash commands!", "Cool cats watch movies.", "Runs on Plex and a little love.", "Help support Mazi by running /donate!", "Now showing, your movies!"]
15 | embed = discord.Embed(title = "Available commands", colour = discord.Colour.from_rgb(229,160,13))
16 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
17 | embed.set_footer(text = random.choice(list))
18 | try:
19 | for command in self.client.tree.get_commands():
20 | if command.extras.get("hidden"):
21 | continue
22 | embed.add_field(name = f"{command.name}:0>", value = command.description, inline = False)
23 | except Exception as e:
24 | print(e)
25 | await interaction.response.send_message(embed=embed)
26 |
27 | async def setup(client):
28 | await client.add_cog(Help(client))
--------------------------------------------------------------------------------
/cogs/host.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 | from firebase_admin import firestore
6 | from decouple import config
7 | from base64 import b64decode
8 | from nacl.secret import SecretBox
9 | from plexapi.server import PlexServer
10 | import requests
11 | import urllib3
12 | import random
13 | import asyncio
14 |
15 | db = firestore.client()
16 | countsdoc = db.collection(u'counts').document(u'counts')
17 | session = requests.Session()
18 | session.verify = False
19 | urllib3.disable_warnings()
20 |
21 | class Host(commands.Cog):
22 |
23 | def __init__(self, client):
24 | self.client = client
25 | @app_commands.command(name="host", description="Host a Plex movie watch together with your friends.")
26 | @app_commands.describe(library="Define the Plex library you would like to host from.", moviechoice = "The exact title of the movie you want to watch. Use /movies or /search to get the title.", timetostart = "The time in minutes to give your friends to join the watch together session. (Default: 5 minutes)")
27 | async def host(self, interaction: Interaction, moviechoice: str, timetostart: int = 5, library: str = None):
28 | await interaction.response.defer() #wait until the bot is finished thinking
29 | discordid = interaction.user.id
30 | # First check if the user is in the database
31 | try:
32 | docs = db.collection(u'userdata').where(u'discordid', u'==', discordid).stream()
33 | empty = True
34 | for doc in docs:
35 | empty = False
36 | data = doc.to_dict()
37 | if empty:
38 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
39 | view = discord.ui.View()
40 | view.add_item(button)
41 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
42 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
43 | await interaction.followup.send(embed=embed, view=view)
44 | return
45 | except:
46 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
47 | view = discord.ui.View()
48 | view.add_item(button)
49 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
50 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
51 | await interaction.followup.send(embed=embed, view=view)
52 | return
53 | # Check if the user has a Plex account linked
54 | try:
55 | plexstatus = data['plex']
56 | except:
57 | button = discord.ui.Button(label="Link your Plex account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
58 | view = discord.ui.View()
59 | view.add_item(button)
60 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex account linked to your Mazi account. Please link one before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
61 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
62 | await interaction.followup.send(embed=embed, view=view)
63 | return
64 | # Check if the user has a Plex server linked
65 | try:
66 | plexserver = data['plexserver']
67 | if len(plexserver) == 0:
68 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
69 | button2 = discord.ui.Button(label="View example server URLS", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
70 | view = discord.ui.View()
71 | view.add_item(button)
72 | view.add_item(button2)
73 | embed = discord.Embed(title = "Plex Server not linked!", description=f"```❌ You don't have a Plex server linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
74 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
75 | await interaction.followup.send(embed=embed, view=view)
76 | return
77 | except:
78 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
79 | button2 = discord.ui.Button(label="View example server URLS", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
80 | view = discord.ui.View()
81 | view.add_item(button)
82 | view.add_item(button2)
83 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex server linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
84 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
85 | await interaction.followup.send(embed=embed, view=view)
86 | return
87 | # Check if the user has a Plex library linked
88 | try:
89 | if library == None:
90 | plexlibrary = data['plexlibrary']
91 | else:
92 | plexlibrary = library
93 | if len(plexlibrary) == 0:
94 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
95 | button2 = discord.ui.Button(label="View example library names", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
96 | view = discord.ui.View()
97 | view.add_item(button)
98 | view.add_item(button2)
99 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex library linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
100 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
101 | await interaction.followup.send(embed=embed, view=view)
102 | return
103 | except:
104 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
105 | button2 = discord.ui.Button(label="View example library names", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
106 | view = discord.ui.View()
107 | view.add_item(button)
108 | view.add_item(button2)
109 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex library linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
110 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
111 | await interaction.followup.send(embed=embed, view=view)
112 | return
113 | if plexstatus and plexserver and plexlibrary:
114 | if library == None:
115 | libraryname = data["plexlibrary"]
116 | else:
117 | libraryname = library
118 | try:
119 | if getHosting(data, moviechoice, libraryname) is None:
120 | embed = discord.Embed(title = "Item not found!", description=f"```❌ {moviechoice} cannot be found in library: {libraryname}. Please try again.```", colour = discord.Colour.from_rgb(229,160,13))
121 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
122 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
123 | await interaction.followup.send(embed=embed)
124 | return
125 | else:
126 | token, machineid, movie, key, plex = getHosting(data, moviechoice, libraryname)
127 | except Exception as e:
128 | print(e)
129 | button = discord.ui.Button(label="Fix accounts", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
130 | button2 = discord.ui.Button(label="View examples", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
131 | view = discord.ui.View()
132 | view.add_item(button)
133 | view.add_item(button2)
134 | embed = discord.Embed(title = "Server not accessible!", description=f"```❌ Your sever is not accessible. This could be due to your server url being not accessible or wrong, or to your server library not being correct. Please view the examples if you need help adding your link or library.```", colour = discord.Colour.from_rgb(229,160,13))
135 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
136 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
137 | await interaction.followup.send(embed = embed, view=view)
138 | return
139 | try:
140 | # create room
141 | counts = countsdoc.get()
142 | previouscount = counts.to_dict()
143 | roomnames = ["Action", "Animation", "Horror", "Adventure", "Comedy", "Romance", "SciFi", "Fantasy", "Musical"]
144 | roomname = random.choice(roomnames)+"-"+str(previouscount["counts"])
145 | msg = await interaction.followup.send(f"```Please join this thread to join Watch Together session: {roomname}.```")
146 | thread = await interaction.channel.create_thread(name=f"Watch Together Session: {roomname}", message=msg, reason="Mazi created Watch Together room.", auto_archive_duration=60)
147 | await thread.send("```To join, please run '/join' in this thread.```")
148 | doc = db.collection(u'counts').document(u'counts')
149 | userid = data["plexid"]
150 | thumb = data["plexthumb"]
151 | title = data["plextitle"]
152 | email = data["plexemail"]
153 | channel = thread.id
154 | discordid = discordid
155 | # add to database
156 | ref = db.collection(u'rooms').document(roomname)
157 | ref.set({
158 | u'Time Started': firestore.SERVER_TIMESTAMP,
159 | u'Thread': channel,
160 | u'MovieKey': key,
161 | u'Server': plexserver,
162 | u'MachineID': machineid,
163 | }, merge=True)
164 | #now add first user
165 | userref = db.collection(u'rooms').document(roomname).collection(u'Users').document(str(discordid))
166 | userref.set({
167 | u'id': userid,
168 | u'thumb': thumb,
169 | u'title': title,
170 | u'email': email,
171 | }, merge=True)
172 | #send message that the room is ready
173 | embed = discord.Embed(title = f"{roomname} is now open to join!", colour = discord.Colour.from_rgb(229,160,13))
174 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
175 | embed.add_field(name = f'{libraryname}', value=f"We are watching {movie.title}!", inline = False)
176 | embed.add_field(name = 'Join Now', value="The room is now open to join! Run /join to join. Make sure you have linked your Plex and Discord accounts.", inline = False)
177 | if timetostart == 0:
178 | embed.set_footer(text = "Room joining will not be disabled. Movie will start immediately. This means that nobody will be able to join.")
179 | else:
180 | embed.set_footer(text = f"Room joining closes in {timetostart} minutes.")
181 | await thread.send(embed = embed)
182 | except Exception as e:
183 | print(e)
184 | print("error creating room")
185 | return
186 | # update counts
187 | try:
188 | doc = db.collection(u'counts').document(u'counts')
189 | counts = doc.get()
190 | previouscount = counts.to_dict()
191 | newcount = int(previouscount['counts']) + 1
192 | doc.update({u'counts': newcount})
193 | except Exception as e:
194 | print(e)
195 | print('Adding count failed')
196 | # timer for room
197 | await asyncio.sleep(timetostart*60)
198 | #start room
199 | try:
200 | #get users from room
201 | ref = db.collection(u'rooms').document(roomname)
202 | doc = ref.get()
203 | data = doc.to_dict()
204 | machineid = data["MachineID"]
205 | moviekey = data["MovieKey"]
206 | #for users in data get user objects and add them to users
207 | users = []
208 | collections = db.collection(u'rooms').document(roomname).collections()
209 | for collection in collections:
210 | for doc in collection.stream():
211 | data = doc.to_dict()
212 | #send request to all users in rooms
213 | username = data["email"]
214 | try:
215 | plex.myPlexAccount().inviteFriend(user=username, server=plex)
216 | except Exception as e:
217 | print(e)
218 | data.pop("email")
219 | users.append(data)
220 | url = f"https://together.plex.tv/rooms?X-Plex-Token={token}"
221 | obj = {
222 | "sourceUri": f"server://{machineid}/com.plexapp.plugins.library/library/metadata/{moviekey}",
223 | "title": movie.title,
224 | "users": users
225 | }
226 | try:
227 | if timetostart == 0:
228 | requests.post(url, json = obj)
229 | else:
230 | db.collection(u'rooms').document(roomname).delete()
231 | await thread.send(f"```Joining for {roomname} is closed. Open Plex on any device and accept the friend request if you are not already friends with the hoster. Then in 5 minutes, join the Watch Together session. {movie.title.capitalize()} will begin shortly.```")
232 | await asyncio.sleep(timetostart*60)
233 | requests.post(url, json = obj)
234 | await thread.send(f"```{roomname} has now started watching {movie.title}!```")
235 | except:
236 | embed = discord.Embed(title = "Server not accessible!", description=f"```❌ Something went wrong and couldn't get a room set up. Please try again later or report this as an error using /project.```", colour = discord.Colour.from_rgb(229,160,13))
237 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
238 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
239 | await interaction.followup.send(embed = embed, view=view)
240 | return
241 | except:
242 | embed = discord.Embed(title = "Server not accessible!", description=f"```❌ Something went wrong and couldn't get a room set up. Please try again later or report this as an error using /project.```", colour = discord.Colour.from_rgb(229,160,13))
243 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
244 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
245 | await interaction.followup.send(embed = embed, view=view)
246 | return
247 | def getHosting(data, moviechoice, libraryname):
248 | try:
249 | encrypted = b64decode(data["plexauth"].encode("utf-8"))
250 | secret_key = config('AESKEY')
251 | box = SecretBox(bytes(secret_key, encoding='utf8'))
252 | plexauth = box.decrypt(encrypted).decode("utf-8")
253 | plexurl = data["plexserver"]
254 | if not (plexurl.startswith('http') or plexurl.startswith('https')):
255 | try:
256 | plexurl = f'http://{plexurl}'
257 | plex = PlexServer(plexurl, plexauth, session=session)
258 | except:
259 | plexurl = f'https://{plexurl}'
260 | plex = PlexServer(plexurl, plexauth, session=session)
261 | else:
262 | plex = PlexServer(plexurl, plexauth, session=session)
263 | token = plex._token
264 | machineid = plex.machineIdentifier
265 | movie = plex.library.section(libraryname).get(moviechoice)
266 | key = movie.ratingKey
267 | return token, machineid, movie, key, plex
268 | except:
269 | return None
270 |
271 | async def setup(client):
272 | await client.add_cog(Host(client))
--------------------------------------------------------------------------------
/cogs/join.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 | from firebase_admin import firestore
6 | import requests
7 | import urllib3
8 |
9 | db = firestore.client()
10 | session = requests.Session()
11 | session.verify = False
12 | urllib3.disable_warnings()
13 |
14 | class Join(commands.Cog):
15 |
16 | def __init__(self, client):
17 | self.client = client
18 |
19 | @app_commands.command(name="join", description="Join an active Plex watch together session room.")
20 | async def search(self, interaction: Interaction):
21 | await interaction.response.defer() #wait until the bot is finished thinking
22 | discordid = interaction.user.id
23 | channel = interaction.channel.id
24 | # First check if the user is in the database
25 | try:
26 | docs = db.collection(u'userdata').where(u'discordid', u'==', discordid).stream()
27 | empty = True
28 | for doc in docs:
29 | empty = False
30 | data = doc.to_dict()
31 | if empty:
32 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
33 | view = discord.ui.View()
34 | view.add_item(button)
35 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
36 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
37 | await interaction.followup.send(embed=embed, view=view)
38 | return
39 | except:
40 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
41 | view = discord.ui.View()
42 | view.add_item(button)
43 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
44 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
45 | await interaction.followup.send(embed=embed, view=view)
46 | return
47 | # Check if the user has a Plex account linked
48 | try:
49 | plexstatus = data['plex']
50 | except:
51 | button = discord.ui.Button(label="Link your Plex account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
52 | view = discord.ui.View()
53 | view.add_item(button)
54 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex account linked to your Mazi account. Please link one before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
55 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
56 | await interaction.followup.send(embed=embed, view=view)
57 | return
58 | if plexstatus:
59 | try:
60 | docs = db.collection(u'rooms').where(u'Thread', u'==', channel).stream()
61 | norooms = True
62 | for rooms in docs:
63 | norooms = False
64 | roomname = rooms.id
65 | if norooms:
66 | embed = discord.Embed(title = "No joinable rooms!", description=f"```❌ No joinable rooms found. Please try again later or in another thread.```", colour = discord.Colour.from_rgb(229,160,13))
67 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
68 | await interaction.followup.send(embed=embed)
69 | return
70 | except:
71 | embed = discord.Embed(title = "No joinable rooms!", description=f"```❌ No joinable rooms found. Please try again later or in another thread.```", colour = discord.Colour.from_rgb(229,160,13))
72 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
73 | await interaction.followup.send(embed=embed)
74 | return
75 | try:
76 | document = db.collection(u'userdata').where(u'discordid', u'==', discordid).stream()
77 | empty = True
78 | for doc in document:
79 | empty = False
80 | data = doc.to_dict()
81 | userid = data["plexid"]
82 | thumb = data["plexthumb"]
83 | title = data["plextitle"]
84 | email = data["plexemail"]
85 | #add to roomname
86 | userref = db.collection(u'rooms').document(roomname).collection(u'Users').document(str(discordid))
87 | userref.set({
88 | u'id': userid,
89 | u'thumb': thumb,
90 | u'title': title,
91 | u'email': email
92 | }, merge=True)
93 | #announce user join
94 | embed = discord.Embed(title = f"{interaction.user.display_name} has joined {roomname}!", description=f"```{interaction.user.display_name} will recieve an invite to {roomname}.```", colour = discord.Colour.from_rgb(229,160,13))
95 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
96 | await interaction.followup.send(embed=embed)
97 | if empty:
98 | embed = discord.Embed(title = "Error joining room!", description=f"```❌ Error joining room. Please try again later or report this error using /project.```", colour = discord.Colour.from_rgb(229,160,13))
99 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
100 | await interaction.followup.send(embed=embed)
101 | except Exception as e:
102 | embed = discord.Embed(title = "Error joining room!", description=f"```❌ Error joining room. Please try again later or report this error using /project.```", colour = discord.Colour.from_rgb(229,160,13))
103 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
104 | await interaction.followup.send(embed=embed)
105 |
106 | async def setup(client):
107 | await client.add_cog(Join(client))
--------------------------------------------------------------------------------
/cogs/link.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 |
6 | class Link(commands.Cog):
7 |
8 | def __init__(self, client):
9 | self.client = client
10 |
11 | @app_commands.command(name="link", description="Link your Plex and Discord accounts.")
12 | async def link(self, interaction: Interaction):
13 |
14 | embed = discord.Embed(title = "Mazi Website", colour = discord.Colour.from_rgb(229,160,13))
15 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
16 | embed.add_field(name = '🔗', value='https://mazi.pw/user', inline = False)
17 | await interaction.response.send_message(embed=embed)
18 |
19 | async def setup(client):
20 | await client.add_cog(Link(client))
--------------------------------------------------------------------------------
/cogs/movies.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 | from firebase_admin import firestore
6 | from decouple import config
7 | from base64 import b64decode
8 | from nacl.secret import SecretBox
9 | from plexapi.server import PlexServer
10 | import requests
11 | import urllib3
12 | import traceback
13 |
14 | db = firestore.client()
15 | session = requests.Session()
16 | session.verify = False
17 | urllib3.disable_warnings()
18 |
19 | class Movies(commands.Cog):
20 |
21 | def __init__(self, client):
22 | self.client = client
23 |
24 | @app_commands.command(name="movies", description="View the all the movies available to watch on your linked Plex account.")
25 | @app_commands.describe(library="Define the Plex library you would like to host from.")
26 | async def search(self, interaction: Interaction, library: str = None):
27 | await interaction.response.defer() #wait until the bot is finished thinking
28 | discordid = interaction.user.id
29 | # First check if the user is in the database
30 | try:
31 | docs = db.collection(u'userdata').where(u'discordid', u'==', discordid).stream()
32 | empty = True
33 | for doc in docs:
34 | empty = False
35 | data = doc.to_dict()
36 | if empty:
37 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
38 | view = discord.ui.View()
39 | view.add_item(button)
40 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
41 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
42 | await interaction.followup.send(embed=embed, view=view)
43 | return
44 | except Exception as e:
45 | print(e)
46 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
47 | view = discord.ui.View()
48 | view.add_item(button)
49 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
50 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
51 | await interaction.followup.send(embed=embed, view=view)
52 | return
53 | # Check if the user has a Plex account linked
54 | try:
55 | plexstatus = data['plex']
56 | except:
57 | button = discord.ui.Button(label="Link your Plex account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
58 | view = discord.ui.View()
59 | view.add_item(button)
60 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex account linked to your Mazi account. Please link one before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
61 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
62 | await interaction.followup.send(embed=embed, view=view)
63 | return
64 | # Check if the user has a Plex server linked
65 | try:
66 | plexserver = data['plexserver']
67 | if len(plexserver) == 0:
68 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
69 | button2 = discord.ui.Button(label="View example server URLS", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
70 | view = discord.ui.View()
71 | view.add_item(button)
72 | view.add_item(button2)
73 | embed = discord.Embed(title = "Plex Server not linked!", description=f"```❌ You don't have a Plex server linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
74 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
75 | await interaction.followup.send(embed=embed, view=view)
76 | return
77 | except:
78 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
79 | button2 = discord.ui.Button(label="View example server URLS", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
80 | view = discord.ui.View()
81 | view.add_item(button)
82 | view.add_item(button2)
83 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex server linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
84 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
85 | await interaction.followup.send(embed=embed, view=view)
86 | return
87 | # Check if the user has a Plex library linked
88 | try:
89 | if library == None:
90 | plexlibrary = data['plexlibrary']
91 | else:
92 | plexlibrary = library
93 | if len(plexlibrary) == 0:
94 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
95 | button2 = discord.ui.Button(label="View example library names", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
96 | view = discord.ui.View()
97 | view.add_item(button)
98 | view.add_item(button2)
99 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex library linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
100 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
101 | await interaction.followup.send(embed=embed, view=view)
102 | return
103 | except:
104 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
105 | button2 = discord.ui.Button(label="View example library names", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
106 | view = discord.ui.View()
107 | view.add_item(button)
108 | view.add_item(button2)
109 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex library linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
110 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
111 | await interaction.followup.send(embed=embed, view=view)
112 | return
113 | if plexstatus and plexserver and plexlibrary:
114 | if library == None:
115 | libraryname = data["plexlibrary"]
116 | else:
117 | libraryname = library
118 | try:
119 | moviefields = []
120 | for movie in getMovies(data, libraryname):
121 | moviefields.append(movie)
122 | movieslist = ""
123 | for items in moviefields:
124 | movieslist += f'{items}\n'
125 | if len(movieslist) == 0:
126 | embed = discord.Embed(title = "No items found!", description=f"```❌ No items found in {libraryname}. Please add items to your library.```", colour = discord.Colour.from_rgb(229,160,13))
127 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
128 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
129 | await interaction.followup.send(embed = embed)
130 | if len(movieslist) > 4096:
131 | moviefields = []
132 | for movie in getMoviesRecent(data, libraryname):
133 | moviefields.append(movie)
134 | movieslist2 = ""
135 | for items in moviefields:
136 | movieslist2 += f'{items}\n'
137 | embed = discord.Embed(title = f"Recently added in {libraryname}", description=movieslist2, colour = discord.Colour.from_rgb(229,160,13))
138 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
139 | embed.set_footer(text = "Mazi couldn't show all items so it is showing the most recently added.")
140 | await interaction.followup.send(embed = embed)
141 | if len(movieslist) < 4096:
142 | embed = discord.Embed(title = f"Available items in {libraryname}", description=movieslist, colour = discord.Colour.from_rgb(229,160,13))
143 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
144 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
145 | await interaction.followup.send(embed = embed)
146 | except:
147 | button = discord.ui.Button(label="Fix accounts", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
148 | button2 = discord.ui.Button(label="View examples", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
149 | view = discord.ui.View()
150 | view.add_item(button)
151 | view.add_item(button2)
152 | embed = discord.Embed(title = "Server not accessible!", description=f"```❌ Your sever is not accessible. This could be due to your server url being not accessible or wrong, or to your server library not being correct. Please view the examples if you need help adding your link or library.```", colour = discord.Colour.from_rgb(229,160,13))
153 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.avatar.url)
154 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
155 | await interaction.followup.send(embed = embed, view=view)
156 | return
157 |
158 | def getMoviesRecent(data, libraryname):
159 | try:
160 | encrypted = b64decode(data["plexauth"].encode("utf-8"))
161 | secret_key = config('AESKEY')
162 | box = SecretBox(bytes(secret_key, encoding='utf8'))
163 | plexauth = box.decrypt(encrypted).decode("utf-8")
164 | plexurl = data["plexserver"]
165 | if not (plexurl.startswith('http') or plexurl.startswith('https')):
166 | try:
167 | plexurl = f'http://{plexurl}'
168 | plex = PlexServer(plexurl, plexauth, session=session)
169 | except:
170 | plexurl = f'https://{plexurl}'
171 | plex = PlexServer(plexurl, plexauth, session=session)
172 | else:
173 | plex = PlexServer(plexurl, plexauth, session=session)
174 | movies = plex.library.section(libraryname)
175 | except Exception as e:
176 | print(e)
177 | return None
178 | list = []
179 | total = 0
180 | for video in movies.recentlyAdded(maxresults=50):
181 | list.append(video.title)
182 | total += len(video.title)
183 | if total > 4096:
184 | break
185 | return(list)
186 | def getMovies(data, libraryname):
187 | try:
188 | encrypted = b64decode(data["plexauth"].encode("utf-8"))
189 | secret_key = config('AESKEY')
190 | box = SecretBox(bytes(secret_key, encoding='utf8'))
191 | plexauth = box.decrypt(encrypted).decode("utf-8")
192 | plexurl = data["plexserver"]
193 | if not (plexurl.startswith('http') or plexurl.startswith('https')):
194 | try:
195 | plexurl = f'http://{plexurl}'
196 | plex = PlexServer(plexurl, plexauth, session=session)
197 | except:
198 | plexurl = f'https://{plexurl}'
199 | plex = PlexServer(plexurl, plexauth, session=session)
200 | else:
201 | plex = PlexServer(plexurl, plexauth, session=session)
202 | movies = plex.library.section(libraryname)
203 | except Exception as e:
204 | traceback.print_exc()
205 | return None
206 | list = []
207 | for video in movies.all():
208 | list.append(video.title)
209 | return(list)
210 |
211 | async def setup(client):
212 | await client.add_cog(Movies(client))
--------------------------------------------------------------------------------
/cogs/ping.py:
--------------------------------------------------------------------------------
1 | from discord.ext import commands
2 | from discord import Interaction
3 | from discord import app_commands
4 |
5 | class Ping(commands.Cog):
6 |
7 | def __init__(self, client):
8 | self.client = client
9 |
10 | @app_commands.command(name="ping", description="View the ping between Discord and Mazi.")
11 | async def donate(self, interaction: Interaction):
12 |
13 | txt = str(f"""```css\nIm not too slow right? {round(self.client.latency * 1000)}ms.```""")
14 | await interaction.response.send_message(txt)
15 |
16 | async def setup(client):
17 | await client.add_cog(Ping(client))
--------------------------------------------------------------------------------
/cogs/project.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 |
6 | class Project(commands.Cog):
7 |
8 | def __init__(self, client):
9 | self.client = client
10 |
11 | @app_commands.command(name="project", description="View the GitHub repository for Mazi.")
12 | async def project(self, interaction: Interaction):
13 |
14 | embed = discord.Embed(title = "Mazi Github", colour = discord.Colour.from_rgb(229,160,13))
15 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
16 | embed.add_field(name = '🔗', value='https://github.com/Wamy-Dev/Mazi', inline = False)
17 | await interaction.response.send_message(embed=embed)
18 |
19 | async def setup(client):
20 | await client.add_cog(Project(client))
--------------------------------------------------------------------------------
/cogs/search.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 | from firebase_admin import firestore
6 | from decouple import config
7 | from nacl.secret import SecretBox
8 | from plexapi.server import PlexServer
9 | import requests
10 | import urllib3
11 | from base64 import b64decode
12 |
13 | db = firestore.client()
14 | session = requests.Session()
15 | session.verify = False
16 | urllib3.disable_warnings()
17 |
18 | class Search(commands.Cog):
19 |
20 | def __init__(self, client):
21 | self.client = client
22 |
23 | @app_commands.command(name="search", description="Search for an item in your Plex library.")
24 | @app_commands.describe(search="The item you want to search for.", library="Define the Plex library you would like to host from.")
25 | async def search(self, interaction: Interaction, search: str, library: str = None):
26 | await interaction.response.defer() #wait until the bot is finished thinking
27 | discordid = interaction.user.id
28 | # First check if the user is in the database
29 | try:
30 | docs = db.collection(u'userdata').where(u'discordid', u'==', discordid).stream()
31 | empty = True
32 | for doc in docs:
33 | empty = False
34 | data = doc.to_dict()
35 | if empty:
36 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
37 | view = discord.ui.View()
38 | view.add_item(button)
39 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
40 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
41 | await interaction.followup.send(embed=embed, view=view)
42 | return
43 | except:
44 | button = discord.ui.Button(label="Link your Discord account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
45 | view = discord.ui.View()
46 | view.add_item(button)
47 | embed = discord.Embed(title = "Discord account not linked!", description=f"```❌ You don't have a Discord account linked to your Mazi account. You also may not have a Mazi account. Please create one if needed. Please link your Discord account before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
48 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
49 | await interaction.followup.send(embed=embed, view=view)
50 | return
51 | # Check if the user has a Plex account linked
52 | try:
53 | plexstatus = data['plex']
54 | except:
55 | button = discord.ui.Button(label="Link your Plex account", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
56 | view = discord.ui.View()
57 | view.add_item(button)
58 | embed = discord.Embed(title = "Plex account not linked!", description=f"```❌ You don't have a Plex account linked to your Mazi account. Please link one before using any features as it is required.```", colour = discord.Colour.from_rgb(229,160,13))
59 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
60 | await interaction.followup.send(embed=embed, view=view)
61 | return
62 | # Check if the user has a Plex server linked
63 | try:
64 | plexserver = data['plexserver']
65 | if len(plexserver) == 0:
66 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
67 | button2 = discord.ui.Button(label="View example server URLS", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
68 | view = discord.ui.View()
69 | view.add_item(button)
70 | view.add_item(button2)
71 | embed = discord.Embed(title = "Plex Server not linked!", description=f"```❌ You don't have a Plex server linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
72 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
73 | await interaction.followup.send(embed=embed, view=view)
74 | return
75 | except:
76 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
77 | button2 = discord.ui.Button(label="View example server URLS", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
78 | view = discord.ui.View()
79 | view.add_item(button)
80 | view.add_item(button2)
81 | embed = discord.Embed(title = "Plex Server not linked!", description=f"```❌ You don't have a Plex server linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
82 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
83 | await interaction.followup.send(embed=embed, view=view)
84 | return
85 | # Check if the user has a Plex library linked
86 | try:
87 | if library == None:
88 | plexlibrary = data['plexlibrary']
89 | else:
90 | plexlibrary = library
91 | if len(plexlibrary) == 0:
92 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
93 | button2 = discord.ui.Button(label="View example library names", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
94 | view = discord.ui.View()
95 | view.add_item(button)
96 | view.add_item(button2)
97 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex library linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
98 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
99 | await interaction.followup.send(embed=embed, view=view)
100 | return
101 |
102 | except:
103 | button = discord.ui.Button(label="Link your Plex server", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
104 | button2 = discord.ui.Button(label="View example library names", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
105 | view = discord.ui.View()
106 | view.add_item(button)
107 | view.add_item(button2)
108 | embed = discord.Embed(title = "Plex Library not linked!", description=f"```❌ You don't have a Plex library linked to your Mazi account. Please add one as it is required to host sessions.```", colour = discord.Colour.from_rgb(229,160,13))
109 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
110 | await interaction.followup.send(embed=embed, view=view)
111 | return
112 |
113 | if plexstatus and plexserver and plexlibrary:
114 | if library == None:
115 | libraryname = data["plexlibrary"]
116 | else:
117 | libraryname = library
118 | try:
119 | moviefields = []
120 | for movie in searchPlex(data, search, libraryname):
121 | moviefields.append(movie)
122 | movieslist = ""
123 | for items in moviefields:
124 | movieslist += f'{items}\n'
125 | if len(moviefields) == 0:
126 | embed = discord.Embed(title = "No results found!", description=f"```❌ No results found in library: {libraryname} when searching for {search}. Please try again later or change search.```", colour = discord.Colour.from_rgb(229,160,13))
127 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
128 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
129 | await interaction.followup.send(embed = embed)
130 | else:
131 | embed = discord.Embed(title = f"Top results in {libraryname}", description=movieslist, colour = discord.Colour.from_rgb(229,160,13))
132 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
133 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
134 | await interaction.followup.send(embed = embed)
135 | except Exception as e:
136 | print(e)
137 | button = discord.ui.Button(label="Fix accounts", style=discord.ButtonStyle.link, url="https://mazi.pw/user")
138 | button2 = discord.ui.Button(label="View examples", style=discord.ButtonStyle.link, url="https://github.com/Wamy-Dev/Mazi/wiki/Examples")
139 | view = discord.ui.View()
140 | view.add_item(button)
141 | view.add_item(button2)
142 | embed = discord.Embed(title = "Server not accessible!", description=f"```❌ Your sever is not accessible. This could be due to your server url being not accessible or wrong, or to your server library not being correct. Please view the examples if you need help adding your link or library.```", colour = discord.Colour.from_rgb(229,160,13))
143 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
144 | embed.set_footer(text = f"{interaction.user.display_name}'s {libraryname}")
145 | await interaction.followup.send(embed = embed, view=view)
146 | return
147 |
148 | def searchPlex(data, title, libraryname):
149 | try:
150 | encrypted = b64decode(data["plexauth"].encode("utf-8"))
151 | secret_key = config('AESKEY')
152 | box = SecretBox(bytes(secret_key, encoding='utf8'))
153 | plexauth = box.decrypt(encrypted).decode("utf-8")
154 | plexurl = data["plexserver"]
155 | if not (plexurl.startswith('http') or plexurl.startswith('https')):
156 | try:
157 | plexurl = f'http://{plexurl}'
158 | plex = PlexServer(plexurl, plexauth, session=session)
159 | except:
160 | plexurl = f'https://{plexurl}'
161 | plex = PlexServer(plexurl, plexauth, session=session)
162 | else:
163 | plex = PlexServer(plexurl, plexauth, session=session)
164 | movies = plex.library.section(libraryname)
165 | except Exception as e:
166 | print(e)
167 | return None
168 | list = []
169 | for video in movies.search(title = title, maxresults = 5):
170 | list.append(video.title)
171 | return(list)
172 |
173 | async def setup(client):
174 | await client.add_cog(Search(client))
--------------------------------------------------------------------------------
/cogs/website.py:
--------------------------------------------------------------------------------
1 | import discord
2 | from discord.ext import commands
3 | from discord import Interaction
4 | from discord import app_commands
5 |
6 | class Website(commands.Cog):
7 |
8 | def __init__(self, client):
9 | self.client = client
10 |
11 | @app_commands.command(name="website", description="View the Mazi website.")
12 | async def website(self, interaction: Interaction):
13 |
14 | embed = discord.Embed(title = "Mazi Website", colour = discord.Colour.from_rgb(229,160,13))
15 | embed.set_author(name = interaction.user.display_name, icon_url = interaction.user.display_avatar.url)
16 | embed.add_field(name = '🔗', value='https://mazi.pw', inline = False)
17 | await interaction.response.send_message(embed=embed)
18 |
19 | async def setup(client):
20 | await client.add_cog(Website(client))
--------------------------------------------------------------------------------
/dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.8-alpine
2 | WORKDIR /Mazi
3 | COPY . .
4 |
5 | RUN apk update && apk add --no-cache git gcc libffi-dev python3-dev libc-dev linux-headers
6 | RUN pip install --no-cache-dir -r requirements.txt
7 | ENV CLIENTTOKEN=
8 | ENV AESKEY=
9 | CMD ["python", "bot.py"]
10 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | git+https://github.com/Rapptz/discord.py
2 | asyncio
3 | python-decouple
4 | quart
5 | firebase-admin
6 | plexapi
7 | pynacl
8 | requests
9 | plexapi
10 | humanfriendly
11 | hypercorn
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wamy-Dev/Mazi/5ccff717bab1b1db16e239e51c3de896053e067d/static/favicon.ico
--------------------------------------------------------------------------------
/static/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: "Ubuntu", sans-serif;
4 | background-color: #212534;
5 | }
6 | h1 {
7 | color: white;
8 | }
9 | p {
10 | color: white;
11 | }
12 | li {
13 | color: white;
14 | list-style: none;
15 | margin-bottom: 10px;
16 | }
17 | .header {
18 | position:relative;
19 | text-align:center;
20 | background: linear-gradient(60deg, #E5A00D 0%, #000000 100%);
21 | color:white;
22 | }
23 | .inner-header {
24 | height: 65vh;
25 | width: 100%;
26 | margin: 0;
27 | padding: 0;
28 | overflow: auto;
29 | }
30 | .flex { /*Flexbox for containers*/
31 | display: flex;
32 | justify-content: center;
33 | text-align: center;
34 | }
35 | .waves {
36 | position:relative;
37 | width: 100%;
38 | height: 15vh;
39 | margin-bottom:-7px; /*Fix for safari gap*/
40 | min-height:100px;
41 | max-height:150px;
42 | }
43 | .content {
44 | position:relative;
45 | height:20vh;
46 | text-align:center;
47 | background-color: white;
48 | }
49 | .footer {
50 | color: black;
51 | }
52 | .items {
53 | margin-top: 20px;
54 | }
55 | .parallax > use {
56 | animation: move-forever 25s cubic-bezier(.55,.5,.45,.5) infinite;
57 | }
58 | .parallax > use:nth-child(1) {
59 | animation-delay: -2s;
60 | animation-duration: 7s;
61 | }
62 | .parallax > use:nth-child(2) {
63 | animation-delay: -3s;
64 | animation-duration: 10s;
65 | }
66 | .parallax > use:nth-child(3) {
67 | animation-delay: -4s;
68 | animation-duration: 13s;
69 | }
70 | .parallax > use:nth-child(4) {
71 | animation-delay: -5s;
72 | animation-duration: 20s;
73 | }
74 | @keyframes move-forever {
75 | 0% {
76 | transform: translate3d(-90px,0,0);
77 | }
78 | 100% {
79 | transform: translate3d(85px,0,0);
80 | }
81 | }
82 | @media (max-width: 768px) {
83 | .waves {
84 | height: 40px;
85 | min-height: 40px;
86 | }
87 | .content {
88 | height: 30vh;
89 | }
90 | h1 {
91 | font-size: 24px;
92 | }
93 | p {
94 | font-size: 15px
95 | }
96 | }
97 | a {
98 | text-decoration: underline;
99 | color: black;
100 | }
--------------------------------------------------------------------------------
/templates/failpage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Mazi Bot Status
11 |
12 |
13 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/templates/statuspage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Mazi Bot Status
11 |
12 |
13 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------