Jfa refresh fix
This commit is contained in:
2
.env
2
.env
@@ -18,6 +18,8 @@ JELLYSEERR_API_KEY=your_api_key_here
|
||||
# JFA-Go
|
||||
ENABLE_JFA=false
|
||||
JFA_URL=http://localhost:8056
|
||||
JFA_USERNAME=yourusername
|
||||
JFA_PASSWORD=yourpassword
|
||||
JFA_API_KEY=your_api_key_here
|
||||
|
||||
# QBittorrent
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# 1.0.7
|
||||
|
||||
- Fixed JFA-GO API keys expiring. The bot now schedules a key refresh
|
||||
|
||||
# 1.0.6
|
||||
|
||||
- Added Progress bar to Active Streams
|
||||
|
||||
@@ -71,6 +71,7 @@ Fill out values in the .env and you're good to go!
|
||||
- `!createinvite` - Create a new JFA invite link
|
||||
- `!listinvites` - List all active JFA invite links
|
||||
- `!deleteinvite <code>` - Delete a specific JFA Invite
|
||||
- `!refreshjfakey` - Refreshes the JFA API Key Forcefully
|
||||
|
||||
***⚙️ Admin Bot Commands***
|
||||
- `!setprefix` - Change the bots command prefix
|
||||
|
||||
140
app.py
140
app.py
@@ -40,6 +40,8 @@ JELLYSEERR_API_KEY = os.getenv("JELLYSEERR_API_KEY", "")
|
||||
|
||||
ENABLE_JFA = os.getenv("ENABLE_JFA", "False").lower() == "true"
|
||||
JFA_URL = os.getenv("JFA_URL")
|
||||
JFA_USERNAME = os.getenv("JFA_USERNAME")
|
||||
JFA_PASSWORD = os.getenv("JFA_PASSWORD")
|
||||
JFA_API_KEY = os.getenv("JFA_API_KEY")
|
||||
|
||||
ENABLE_QBITTORRENT = os.getenv("ENABLE_QBITTORRENT", "False").lower() == "true"
|
||||
@@ -356,6 +358,73 @@ def progress_bar(progress: float, length: int = 20) -> str:
|
||||
bar = '█' * filled_length + '░' * (length - filled_length)
|
||||
return f"[{bar}] {progress*100:.2f}%"
|
||||
|
||||
# =====================
|
||||
# JFA-GO HELPERS
|
||||
# =====================
|
||||
|
||||
def refresh_jfa_token() -> bool:
|
||||
"""
|
||||
Authenticate to JFA-Go with username/password (Basic auth) against /token/login,
|
||||
write the returned token to .env (JFA_TOKEN and JFA_API_KEY), and reload env.
|
||||
Returns True on success.
|
||||
"""
|
||||
global JFA_TOKEN, JFA_API_KEY
|
||||
|
||||
if not (JFA_URL and JFA_USERNAME and JFA_PASSWORD):
|
||||
print("[JFA] Missing JFA_URL/JFA_USERNAME/JFA_PASSWORD in environment.")
|
||||
return False
|
||||
|
||||
url = JFA_URL.rstrip("/") + "/token/login"
|
||||
headers = {"accept": "application/json"}
|
||||
|
||||
try:
|
||||
# Option A: let requests build the Basic header
|
||||
r = requests.get(url, auth=(JFA_USERNAME, JFA_PASSWORD), headers=headers, timeout=10)
|
||||
|
||||
# If you prefer to build the header manually (exactly like your curl), use:
|
||||
# creds = f"{JFA_USERNAME}:{JFA_PASSWORD}".encode()
|
||||
# b64 = base64.b64encode(creds).decode()
|
||||
# headers["Authorization"] = f"Basic {b64}"
|
||||
# r = requests.get(url, headers=headers, timeout=10)
|
||||
|
||||
if r.status_code != 200:
|
||||
print(f"[JFA] token login failed: {r.status_code} - {r.text}")
|
||||
return False
|
||||
|
||||
data = r.json() if r.text else {}
|
||||
# try common token fields
|
||||
token = (
|
||||
data.get("token")
|
||||
or data.get("access_token")
|
||||
or data.get("jwt")
|
||||
or data.get("api_key")
|
||||
or data.get("data") # sometimes nested
|
||||
)
|
||||
|
||||
# If API returns {"token": "<token>"} -> good. If it returns a wrapped structure,
|
||||
# try to handle a couple of other shapes:
|
||||
if not token:
|
||||
# if response is {'success': True} or {'invites':...} then no token present
|
||||
# print for debugging
|
||||
print("[JFA] token not found in response JSON:", data)
|
||||
return False
|
||||
|
||||
# Persist token to .env under both names (compatibility)
|
||||
_update_env_key("JFA_TOKEN", token)
|
||||
_update_env_key("JFA_API_KEY", token)
|
||||
|
||||
# Update in-memory values and reload env
|
||||
JFA_TOKEN = token
|
||||
JFA_API_KEY = token
|
||||
load_dotenv(override=True)
|
||||
|
||||
print("[JFA] Successfully refreshed token and updated .env")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[JFA] Exception while refreshing token: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
# =====================
|
||||
# DISCORD HELPERS
|
||||
# =====================
|
||||
@@ -437,6 +506,26 @@ def create_trial_jellyfin_user(username, password):
|
||||
print(f"[Jellyfin] Trial user creation failed. Status: {response.status_code}, Response: {response.text}")
|
||||
return None
|
||||
|
||||
def _update_env_key(key: str, value: str, env_path: str = ".env"):
|
||||
"""Update or append key=value in .env (keeps file order)."""
|
||||
lines = []
|
||||
found = False
|
||||
try:
|
||||
with open(env_path, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
except FileNotFoundError:
|
||||
lines = []
|
||||
|
||||
with open(env_path, "w", encoding="utf-8") as f:
|
||||
for line in lines:
|
||||
if line.strip().startswith(f"{key}="):
|
||||
f.write(f"{key}={value}\n")
|
||||
found = True
|
||||
else:
|
||||
f.write(line)
|
||||
if not found:
|
||||
f.write(f"{key}={value}\n")
|
||||
|
||||
|
||||
# =====================
|
||||
# EVENTS
|
||||
@@ -760,6 +849,27 @@ async def clearinvites(ctx):
|
||||
print(f"[clearinvites] Error: {e}", exc_info=True)
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def refreshjfakey(ctx):
|
||||
"""Admin-only: Force refresh the JFA-Go API key using username/password auth."""
|
||||
if not has_admin_role(ctx.author):
|
||||
await ctx.send("❌ You don’t have permission to use this command.")
|
||||
return
|
||||
|
||||
if not ENABLE_JFA:
|
||||
await ctx.send("⚠️ JFA-Go integration is disabled in the configuration.")
|
||||
return
|
||||
|
||||
await ctx.send("🔁 Attempting to refresh JFA token...")
|
||||
success = refresh_jfa_token()
|
||||
if success:
|
||||
await ctx.send("✅ Successfully refreshed the JFA-Go API token and updated `.env`")
|
||||
log_event(f"Admin {ctx.author} forced a JFA API Token refresh")
|
||||
else:
|
||||
await ctx.send("❌ Failed to refresh the JFA-Go API token. Check bot logs for details.")
|
||||
log_event(f"Admin {ctx.author} attempted JFA API Token refresh but failed")
|
||||
|
||||
|
||||
@bot.command()
|
||||
async def trialaccount(ctx, username: str = None, password: str = None):
|
||||
"""Create a 24-hour trial Jellyfin account. DM-only, one-time per user."""
|
||||
@@ -1418,7 +1528,8 @@ async def help_command(ctx):
|
||||
jfa_cmds = [
|
||||
f"`{PREFIX}createinvite` - Create a new JFA invite link",
|
||||
f"`{PREFIX}listinvites` - List all active JFA invite links",
|
||||
f"`{PREFIX}deleteinvite <code>` - Delete a specific JFA invite"
|
||||
f"`{PREFIX}deleteinvite <code>` - Delete a specific JFA invite",
|
||||
f"`{PREFIX}refreshjfakey` - Refreshes the JFA API Key Forcefully"
|
||||
]
|
||||
embed.add_field(name="🔑 JFA Commands", value="\n".join(jfa_cmds), inline=False)
|
||||
|
||||
@@ -1570,6 +1681,30 @@ async def cleanup_task():
|
||||
except Exception as e:
|
||||
print(f"[Cleanup] Failed to send removed message to sync channel: {e}")
|
||||
|
||||
# =====================
|
||||
# JFA-Go Scheduled Token Refresh
|
||||
# =====================
|
||||
if ENABLE_JFA:
|
||||
|
||||
@tasks.loop(hours=18)
|
||||
async def refresh_jfa_loop():
|
||||
success = refresh_jfa_token()
|
||||
if success:
|
||||
log_event("[JFA] Successfully refreshed token (scheduled loop).")
|
||||
else:
|
||||
log_event("[JFA] Failed to refresh token (scheduled loop).")
|
||||
|
||||
@refresh_jfa_loop.before_loop
|
||||
async def before_refresh_jfa_loop():
|
||||
await bot.wait_until_ready()
|
||||
log_event("[JFA] Token refresh loop waiting until bot is ready.")
|
||||
|
||||
# Start the loop inside on_ready to ensure event loop exists
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
if not refresh_jfa_loop.is_running():
|
||||
refresh_jfa_loop.start()
|
||||
log_event(f"Bot is ready. Logged in as {bot.user}")
|
||||
|
||||
@tasks.loop(hours=1)
|
||||
async def check_for_updates():
|
||||
@@ -1615,6 +1750,9 @@ async def on_ready():
|
||||
if not cleanup_task.is_running():
|
||||
cleanup_task.start()
|
||||
|
||||
if not refresh_jfa_loop.is_running():
|
||||
refresh_jfa_loop.start()
|
||||
|
||||
if not check_for_updates.is_running():
|
||||
check_for_updates.start()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user