Kemono-Downloader/src/core/discord_client.py

89 lines
3.5 KiB
Python
Raw Normal View History

2025-08-07 21:42:04 -07:00
import time
2025-08-17 05:51:25 -07:00
import cloudscraper
2025-08-07 21:42:04 -07:00
import json
2025-08-17 05:51:25 -07:00
def fetch_server_channels(server_id, logger=print, cookies_dict=None):
2025-08-07 21:42:04 -07:00
"""
2025-08-17 05:51:25 -07:00
Fetches all channels for a given Discord server ID from the API.
Uses cloudscraper to bypass Cloudflare.
2025-08-07 21:42:04 -07:00
"""
2025-08-17 05:51:25 -07:00
api_url = f"https://kemono.cr/api/v1/discord/server/{server_id}"
logger(f" Fetching channels for server: {api_url}")
2025-08-10 09:16:31 -07:00
2025-08-17 05:51:25 -07:00
scraper = cloudscraper.create_scraper()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Referer': f'https://kemono.cr/discord/server/{server_id}',
'Accept': 'text/css'
}
2025-08-07 21:42:04 -07:00
2025-08-17 05:51:25 -07:00
try:
response = scraper.get(api_url, headers=headers, cookies=cookies_dict, timeout=30)
response.raise_for_status()
channels = response.json()
if isinstance(channels, list):
logger(f" ✅ Found {len(channels)} channels for server {server_id}.")
return channels
return None
except Exception as e:
logger(f" ❌ Error fetching server channels for {server_id}: {e}")
return None
def fetch_channel_messages(channel_id, logger=print, cancellation_event=None, pause_event=None, cookies_dict=None):
2025-08-07 21:42:04 -07:00
"""
2025-08-17 05:51:25 -07:00
A generator that fetches all messages for a specific Discord channel, handling pagination.
Uses cloudscraper and proper headers to bypass server protection.
2025-08-07 21:42:04 -07:00
"""
2025-08-17 05:51:25 -07:00
scraper = cloudscraper.create_scraper()
base_url = f"https://kemono.cr/api/v1/discord/channel/{channel_id}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Referer': f'https://kemono.cr/discord/channel/{channel_id}',
'Accept': 'text/css'
}
2025-08-07 21:42:04 -07:00
2025-08-17 05:51:25 -07:00
offset = 0
page_size = 150
while True:
if cancellation_event and cancellation_event.is_set():
logger(" Discord message fetching cancelled.")
break
2025-08-07 21:42:04 -07:00
if pause_event and pause_event.is_set():
2025-08-17 05:51:25 -07:00
logger(" Discord message fetching paused...")
2025-08-07 21:42:04 -07:00
while pause_event.is_set():
2025-08-17 05:51:25 -07:00
if cancellation_event and cancellation_event.is_set():
break
2025-08-07 21:42:04 -07:00
time.sleep(0.5)
2025-08-17 05:51:25 -07:00
if not (cancellation_event and cancellation_event.is_set()):
logger(" Discord message fetching resumed.")
2025-08-07 21:42:04 -07:00
2025-08-17 05:51:25 -07:00
paginated_url = f"{base_url}?o={offset}"
2025-08-07 21:42:04 -07:00
logger(f" Fetching messages from API: page starting at offset {offset}")
try:
2025-08-17 05:51:25 -07:00
response = scraper.get(paginated_url, headers=headers, cookies=cookies_dict, timeout=30)
2025-08-07 21:42:04 -07:00
response.raise_for_status()
messages_batch = response.json()
if not messages_batch:
logger(f" ✅ Reached end of messages for channel {channel_id}.")
break
logger(f" Fetched {len(messages_batch)} messages...")
yield messages_batch
if len(messages_batch) < page_size:
logger(f" ✅ Last page of messages received for channel {channel_id}.")
break
offset += page_size
2025-08-17 05:51:25 -07:00
time.sleep(0.5) # Be respectful to the API
2025-08-07 21:42:04 -07:00
2025-08-17 05:51:25 -07:00
except (cloudscraper.exceptions.CloudflareException, json.JSONDecodeError) as e:
2025-08-07 21:42:04 -07:00
logger(f" ❌ Error fetching messages at offset {offset}: {e}")
2025-08-17 05:51:25 -07:00
break
except Exception as e:
logger(f" ❌ An unexpected error occurred while fetching messages: {e}")
break