Add Overseerr cog

This commit is contained in:
2024-09-30 20:08:21 -04:00
parent c319fdeab9
commit d703658be3
4 changed files with 215 additions and 0 deletions

21
overseerr/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
Creative Commons Attribution 4.0 International License
This work is licensed under the Creative Commons Attribution 4.0 International License.
To view a copy of this license, visit:
http://creativecommons.org/licenses/by/4.0/
or send a letter to:
Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
The full text of the license can be found at:
https://creativecommons.org/licenses/by/4.0/legalcode
You are free to:
- Share — copy and redistribute the material in any medium or format
- Adapt — remix, transform, and build upon the material for any purpose, even commercially.
Under the following terms:
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

35
overseerr/README.md Normal file
View File

@@ -0,0 +1,35 @@
# Overseerr Cog for Red Discord Bot
This cog allows interaction with [Overseerr](https://overseerr.dev/) directly from Discord. Users can search for movies or TV shows, request them, and have admins approve requests. It's designed for servers with Overseerr set up for managing media requests.
## Features
- **Set Overseerr URL and API key**: Admins can configure the Overseerr URL and API key for API interactions.
- **Search and request media**: Users can search for movies or TV shows and request them directly in Discord.
- **Media availability status**: The cog checks if media is already available or has been requested before making new requests.
- **Approve requests**: Admins with the appropriate role can approve Overseerr requests within Discord.
## Commands
### Admin Commands
- **`[p]setoverseerr <url> <api_key>`**
- Set the Overseerr URL and API key for the bot to communicate with Overseerr.
- Example: `[p]setoverseerr https://my.overseerr.url abcdefghijklmnop`
- **`[p]setadminrole <role_name>`**
- Set the name of the admin role that is allowed to approve Overseerr requests.
- Example: `[p]setadminrole Overseerr Admin`
### User Commands
- **`[p]request <media name>`**
- Search for a movie or TV show and request it if it's not already available or requested.
- Example: `[p]request The Matrix`
- **`[p]approve <request_id>`**
- Approve a media request by its request ID (requires the admin role).
- Example: `[p]approve 123`
## Installation
1. Add the cog to your Red instance:
```bash
[p]load overseerr

7
overseerr/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
from .overseerr import Overseerr
__red_end_user_data_statement__ = "This allows users to make requests to Overseerr and Admins can approve them."
async def setup(bot):
await bot.add_cog(Overseerr(bot))

152
overseerr/overseerr.py Normal file
View File

@@ -0,0 +1,152 @@
from redbot.core import commands, Config
from redbot.core.bot import Red
import asyncio
import json
class Overseerr(commands.Cog):
def __init__(self, bot: Red):
self.bot = bot
self.config = Config.get_conf(self, identifier=1234567890)
default_global = {
"overseerr_url": None,
"overseerr_api_key": None,
"admin_role_name": "Overseerr Admin"
}
self.config.register_global(**default_global)
@commands.command()
@commands.admin()
async def setoverseerr(self, ctx: commands.Context, url: str, api_key: str):
"""Set the Overseerr URL and API key."""
await self.config.overseerr_url.set(url)
await self.config.overseerr_api_key.set(api_key)
await ctx.send("Overseerr URL and API key have been set.")
@commands.command()
@commands.admin()
async def setadminrole(self, ctx: commands.Context, role_name: str):
"""Set the admin role name for Overseerr approvals."""
await self.config.admin_role_name.set(role_name)
await ctx.send(f"Admin role for Overseerr approvals set to {role_name}.")
async def get_media_status(self, media_id, media_type):
overseerr_url = await self.config.overseerr_url()
overseerr_api_key = await self.config.overseerr_api_key()
url = f"{overseerr_url}/api/v1/{'movie' if media_type == 'movie' else 'tv'}/{media_id}"
headers = {"X-Api-key": overseerr_api_key}
async with self.bot.session.get(url, headers=headers) as resp:
if resp.status == 200:
data = await resp.json()
status = "Available" if data.get('mediaInfo', {}).get('status') == 3 else "Not Available"
if data.get('request'):
status += " (Requested)"
return status
return "Status Unknown"
@commands.command()
async def request(self, ctx: commands.Context, *, query: str):
"""Search and request a movie or TV show on Overseerr."""
overseerr_url = await self.config.overseerr_url()
overseerr_api_key = await self.config.overseerr_api_key()
if not overseerr_url or not overseerr_api_key:
await ctx.send("Overseerr is not configured. Please ask an admin to set it up.")
return
search_url = f"{overseerr_url}/api/v1/search"
request_url = f"{overseerr_url}/api/v1/request"
headers = {
"X-Api-Key": overseerr_api_key,
"Content-Type": "application/json"
}
# Search for the movie or TV show
async with self.bot.session.get(search_url, headers=headers, params={"query": query}) as resp:
search_results = await resp.json()
if not search_results['results']:
await ctx.send(f"No results found for '{query}'.")
return
# Display search results with availability status
result_message = "Please choose a result by reacting with the corresponding number:\n\n"
for i, result in enumerate(search_results['results'][:5], start=1):
media_type = result['mediaType']
status = await self.get_media_status(result['id'], media_type)
result_message += f"{i}. [{media_type.upper()}] {result['title']} ({result.get('releaseDate', 'N/A')}) - {status}\n"
result_msg = await ctx.send(result_message)
# Add reaction options
reactions = ['1', '2', '3', '4', '5']
for i in range(min(len(search_results['results']), 5)):
await result_msg.add_reaction(reactions[i])
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in reactions
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send("Search timed out. Please try again.")
return
selected_index = reactions.index(str(reaction.emoji))
selected_result = search_results['results'][selected_index]
media_type = selected_result['mediaType']
# Check if the media is already available or requested
status = await self.get_media_status(selected_result['id'], media_type)
if "Available" in status:
await ctx.send(f"'{selected_result['title']}' is already available. No need to request!")
return
elif "Requested" in status:
await ctx.send(f"'{selected_result['title']}' has already been requested. No need to request again!")
return
# Make the request
request_data = {
"mediaId": selected_result['id'],
"mediaType": media_type
}
async with self.bot.session.post(request_url, headers=headers, json=request_data) as resp:
if resp.status == 200:
response_data = await resp.json()
request_id = response_data.get('id')
await ctx.send(f"Successfully requested {media_type} '{selected_result['title']}'! Request ID: {request_id}")
else:
await ctx.send(f"Failed to request {media_type} '{selected_result['title']}'. Please try again later.")
@commands.command()
async def approve(self, ctx: commands.Context, request_id: int):
"""Approve a request on Overseerr."""
admin_role_name = await self.config.admin_role_name()
if not any(role.name == admin_role_name for role in ctx.author.roles):
await ctx.send(f"You need the '{admin_role_name}' role to approve requests.")
return
overseerr_url = await self.config.overseerr_url()
overseerr_api_key = await self.config.overseerr_api_key()
if not overseerr_url or not overseerr_api_key:
await ctx.send("Overseerr is not configured. Please ask an admin to set it up.")
return
approve_url = f"{overseerr_url}/api/v1/request/{request_id}/approve"
headers = {
"X-Api-Key": overseerr_api_key,
"Content-Type": "application/json"
}
async with self.bot.session.post(approve_url, headers=headers) as resp:
if resp.status == 200:
await ctx.send(f"Request {request_id} has been approved!")
else:
await ctx.send(f"Failed to approve request {request_id}. Please check the request ID and try again.")
def setup(bot: Red):
bot.add_cog(Overseerr(bot))