e.lovinator.space/src/e/discord.py

76 lines
1.9 KiB
Python

from __future__ import annotations
from datetime import datetime # ruff: ignore[typing-only-standard-library-import]
from enum import StrEnum
from ipaddress import IPv4Network # ruff: ignore[typing-only-standard-library-import]
import wreq
from loguru import logger
from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field
from wreq import Client
from wreq import Emulation
class Service(StrEnum):
"""What service the prefix is for."""
API = "api"
MEDIA = "media"
class Prefix(BaseModel):
"""IP and if the IP is tied to API or media."""
model_config = ConfigDict(populate_by_name=True)
ipv4_prefix: IPv4Network = Field(alias="ipv4Prefix")
services: list[Service]
class DiscordIPs(BaseModel):
"""Discord IPs."""
model_config = ConfigDict(populate_by_name=True)
creation_time: datetime = Field(alias="creationTime")
"""\"creationTime\": \"2026-08-04T18:01:19Z\""""
sync_token: str = Field(alias="syncToken")
"""\"syncToken\": \"4ce19406\""""
notes: str
"""\"notes\": \"discord egress\""""
prefixes: list[Prefix]
"""\"prefixes\": [
{"ipv4Prefix": "104.196.222.45/32", "services": ["media"]},
{"ipv4Prefix": "34.138.218.50/32", "services": ["api"]},
]"""
async def get_discord_ips() -> DiscordIPs:
"""Grab the IPs that Discord uses.
Returns:
DiscordIPs: The IPs that Discord uses.
"""
url = "https://cdn.discordapp.com/ipranges/discord.json"
client = Client(emulation=Emulation.Chrome149)
resp: wreq.Response = await client.get(url)
data: str = await resp.text()
parsed_data: DiscordIPs = DiscordIPs.model_validate_json(data)
return parsed_data
if __name__ == "__main__":
import asyncio
# Log the IPs that Discord uses.
ips: DiscordIPs = asyncio.run(get_discord_ips())
for ip in ips.prefixes:
for service in ip.services:
logger.info(f"[{service}]\t{ip.ipv4_prefix}")