From 61fb6cb38298214d8fd633ac257e889df4bfe8b1 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Tue, 5 Dec 2023 21:12:33 +0000 Subject: [PATCH] add whois --- cogs/events.py | 21 ++- cogs/extremism.py | 18 +-- cogs/mcdonalds.py | 35 ++--- cogs/other.py | 318 ++++++++++++++++++---------------------------- cogs/timetable.py | 2 +- main.py | 19 ++- utils/client.py | 4 +- utils/views.py | 9 +- web/server.py | 12 +- 9 files changed, 177 insertions(+), 261 deletions(-) diff --git a/cogs/events.py b/cogs/events.py index 61676fc..d54ab2b 100644 --- a/cogs/events.py +++ b/cogs/events.py @@ -20,9 +20,9 @@ import discord import httpx import pydantic from bs4 import BeautifulSoup -from config import guilds from discord.ext import commands, pages, tasks +from config import guilds from utils import Student, console, get_or_none try: @@ -319,15 +319,16 @@ class Events(commands.Cog): return if message.channel.name == "femboy-hole": + def generate_payload(_message: discord.Message) -> MessagePayload: _payload = MessagePayload( message_id=_message.id, author=_message.author.name, is_automated=_message.author.bot or _message.author.system, avatar=_message.author.display_avatar.with_static_format("webp").with_size(512).url, - content=_message.content or '', - clean_content=str(_message.clean_content or ''), - at=_message.created_at.timestamp() + content=_message.content or "", + clean_content=str(_message.clean_content or ""), + at=_message.created_at.timestamp(), ) for attachment in _message.attachments: _payload.attachments.append( @@ -338,7 +339,7 @@ class Events(commands.Cog): size=attachment.size, width=attachment.width, height=attachment.height, - content_type=attachment.content_type + content_type=attachment.content_type, ) ) if _message.reference is not None and _message.reference.cached_message: @@ -346,11 +347,7 @@ class Events(commands.Cog): _payload.reply_to = generate_payload(_message.reference.cached_message) except RecursionError: _payload.reply_to = None - logging.warning( - "Failed to generate reply payload for message %s", - _message.id, - exc_info=True - ) + logging.warning("Failed to generate reply payload for message %s", _message.id, exc_info=True) return _payload payload = generate_payload(message) @@ -449,7 +446,9 @@ class Events(commands.Cog): r"bus((s)?es)?\W*$": {"file": discord.File(assets / "bus.m4a")}, r"^DoH$": {"content": "DoH: Domain Name Service over Hyper Text Transfer Protocol Secure"}, r"^DoT$": {"content": "DoT: Domain Name Service over Transport Layer Security"}, - r"^DoQ$": {"content": "DoQ: Domain Name Service over Quick User Datagram Protocol Internet Connections"}, + r"^DoQ$": { + "content": "DoQ: Domain Name Service over Quick User Datagram Protocol Internet Connections" + }, r"^(Do)?DTLS$": {"content": "DoDTLS: Domain Name Service over Datagram Transport Layer Security"}, } # Stop responding to any bots diff --git a/cogs/extremism.py b/cogs/extremism.py index ba08d2b..30723f9 100644 --- a/cogs/extremism.py +++ b/cogs/extremism.py @@ -138,11 +138,11 @@ class Extremism(commands.Cog): @commands.slash_command(name="decorate") async def decorate( - self, - ctx: discord.ApplicationContext, - decoration_url: str, - user: discord.User = None, - # animated: bool = True + self, + ctx: discord.ApplicationContext, + decoration_url: str, + user: discord.User = None, + # animated: bool = True ): """Decorates an avatar with a decoration.""" if user is None: @@ -162,9 +162,7 @@ class Extremism(commands.Cog): size = 640 size = min(640, max(160, size)) - decoration_url = urlparse(decoration_url)._replace( - query="?size={!s}&passthrough=true".format(size) - ).geturl() + decoration_url = urlparse(decoration_url)._replace(query="?size={!s}&passthrough=true".format(size)).geturl() # Download the decoration try: @@ -199,9 +197,7 @@ class Extremism(commands.Cog): # discord.File(decoration_bio, "decoration.png"), discord.File(img_bytes, filename="decorated." + ext) ] - await ctx.respond( - files=files - ) + await ctx.respond(files=files) def setup(bot): diff --git a/cogs/mcdonalds.py b/cogs/mcdonalds.py index 06a66d3..40caeff 100644 --- a/cogs/mcdonalds.py +++ b/cogs/mcdonalds.py @@ -5,7 +5,6 @@ import re import typing import aiosqlite - import discord from discord.ext import commands @@ -42,7 +41,7 @@ class McDataBase: """ SELECT since, started FROM breaks WHERE user_id = ?; """, - (user_id,) + (user_id,), ) as cursor: return await cursor.fetchone() @@ -54,7 +53,7 @@ class McDataBase: INSERT INTO breaks (user_id, since, started) VALUES (?, ?, ?) ON CONFLICT(user_id) DO UPDATE SET since = excluded.since, started = excluded.started """, - (user_id, since, started) + (user_id, since, started), ) async def remove_break(self, user_id: int) -> None: @@ -63,7 +62,7 @@ class McDataBase: """ DELETE FROM breaks WHERE user_id = ?; """, - (user_id,) + (user_id,), ) await self.set_cooldown(user_id, now) @@ -72,7 +71,7 @@ class McDataBase: """ SELECT expires FROM cooldowns WHERE user_id = ?; """, - (user_id,) + (user_id,), ) as cursor: return await cursor.fetchone() @@ -82,7 +81,7 @@ class McDataBase: INSERT INTO cooldowns (user_id, expires) VALUES (?, ?) ON CONFLICT(user_id) DO UPDATE SET expires = excluded.expires; """, - (user_id, expires) + (user_id, expires), ) async def remove_cooldown(self, user_id: int) -> None: @@ -90,7 +89,7 @@ class McDataBase: """ DELETE FROM cooldowns WHERE user_id = ?; """, - (user_id,) + (user_id,), ) async def __aenter__(self) -> "McDataBase": @@ -123,35 +122,27 @@ class McDonaldsCog(commands.Cog): if (last_info := await db.get_break(author.id)) is not None: if message.content.upper() != "MCDONALDS!": if (message.created_at.timestamp() - last_info[1]) > 300: - self.log.debug( - "Ad break expired for %s (%s).", author.name, author.id - ) + self.log.debug("Ad break expired for %s (%s).", author.name, author.id) await db.remove_break(author.id) await message.reply( f"Thank you for your patience during this commercial break. You may now resume your" f" activity.", - delete_after=120 + delete_after=120, ) elif (message.created_at.timestamp() - last_info[0]) > 10: self.log.info( - "Deleting message %r by %r as they need to skip the ad first.", - message, - author + "Deleting message %r by %r as they need to skip the ad first.", message, author ) await message.delete(delay=0) await message.channel.send( - f"{message.author.mention} Please say `MCDONALDS!` to end commercial.", - delete_after=30 + f"{message.author.mention} Please say `MCDONALDS!` to end commercial.", delete_after=30 ) await db.set_break(author.id, message.created_at.timestamp()) elif message.author.bot is False: self.log.info("%r skipped the ad break.", author) await db.remove_break(author.id) - await message.reply( - "Thank you. You may now resume your activity.", - delete_after=120 - ) + await message.reply("Thank you. You may now resume your activity.", delete_after=120) @commands.user_command(name="Commercial Break") @commands.cooldown(2, 60, commands.BucketType.member) @@ -184,7 +175,7 @@ class McDonaldsCog(commands.Cog): f"{member.mention} Commercial break! Please say `MCDONALDS!` to end commercial.\n" f"*This commercial break is sponsored by {ctx.user.mention}.*", delete_after=300, - allowed_mentions=discord.AllowedMentions(users=True, roles=False, everyone=False) + allowed_mentions=discord.AllowedMentions(users=True, roles=False, everyone=False), ) await ctx.respond("Commercial break started.", ephemeral=True) await ctx.delete(delay=120) @@ -199,7 +190,7 @@ class McDonaldsCog(commands.Cog): f"{member.mention} Commercial break! Please say `MCDONALDS!` to end commercial.\n" f"*This commercial break is sponsored by {ctx.author.mention}.*", delete_after=300, - allowed_mentions=discord.AllowedMentions(users=True, roles=False, everyone=False) + allowed_mentions=discord.AllowedMentions(users=True, roles=False, everyone=False), ) await ctx.message.delete(delay=120) diff --git a/cogs/other.py b/cogs/other.py index 810aaee..7a39b69 100644 --- a/cogs/other.py +++ b/cogs/other.py @@ -2,19 +2,16 @@ import asyncio import fnmatch import functools import glob +import hashlib import io -import logging -import pathlib - -import openai -import pydub import json +import logging import math import os +import pathlib import random import re import shutil -import hashlib import subprocess import sys import tempfile @@ -33,7 +30,9 @@ import aiohttp import discord import dns.resolver import httpx +import openai import psutil +import pydub import pytesseract import pyttsx3 from discord import Interaction @@ -75,9 +74,7 @@ except Exception as _pyttsx3_err: VOICES = [] -async def ollama_stream_reader(response: httpx.Response) -> typing.AsyncGenerator[ - dict[str, str | int | bool], None -]: +async def ollama_stream_reader(response: httpx.Response) -> typing.AsyncGenerator[dict[str, str | int | bool], None]: async for chunk in response.aiter_lines(): # Each line is a JSON string try: @@ -936,15 +933,12 @@ class OtherCog(commands.Cog): "merge_output_format": "webm/mp4/mov/flv/avi/ogg/m4a/wav/mp3/opus/mka/mkv", "source_address": "0.0.0.0", "cookiefile": str(real_cookies_txt.resolve().absolute()), - "concurrent_fragment_downloads": 4 + "concurrent_fragment_downloads": 4, } description = "" proxy_url = "socks5://localhost:1090" try: - proxy_down = await asyncio.wait_for( - self.check_proxy("socks5://localhost:1090"), - timeout=10 - ) + proxy_down = await asyncio.wait_for(self.check_proxy("socks5://localhost:1090"), timeout=10) if proxy_down > 0: if proxy_down == 1: description += ":warning: (SHRoNK) Proxy check leaked IP - trying backup proxy.\n" @@ -953,10 +947,7 @@ class OtherCog(commands.Cog): else: description += ":warning: (SHRoNK) Unknown proxy error - trying backup proxy.\n" - proxy_down = await asyncio.wait_for( - self.check_proxy("socks5://localhost:1080"), - timeout=10 - ) + proxy_down = await asyncio.wait_for(self.check_proxy("socks5://localhost:1080"), timeout=10) if proxy_down > 0: if proxy_down == 1: description += ":warning: (NexBox) Proxy check leaked IP..\n" @@ -1011,20 +1002,14 @@ class OtherCog(commands.Cog): "* Chosen format: `%s` (`%s`)" % (chosen_format, chosen_format_id), ) if format_note: - lines.append( - "* Format note: %r" % format_note - ) + lines.append("* Format note: %r" % format_note) if final_extension: - lines.append( - "* File extension: " + final_extension - ) + lines.append("* File extension: " + final_extension) if resolution: _s = resolution if fps: _s += " @ %s FPS" % fps - lines.append( - "* Resolution: " + _s - ) + lines.append("* Resolution: " + _s) if vcodec or acodec: lines.append("%s+%s" % (vcodec or "N/A", acodec or "N/A")) @@ -1761,17 +1746,18 @@ class OtherCog(commands.Cog): @commands.slash_command() @commands.max_concurrency(3, commands.BucketType.user, wait=False) async def ollama( - self, - ctx: discord.ApplicationContext, - model: str = "orca-mini", - query: str = None, - context: str = None, - server: str = "auto" + self, + ctx: discord.ApplicationContext, + model: str = "orca-mini", + query: str = None, + context: str = None, + server: str = "auto", ): """:3""" with open("./assets/ollama-prompt.txt") as file: system_prompt = file.read().replace("\n", " ").strip() if query is None: + class InputPrompt(discord.ui.Modal): def __init__(self, is_owner: bool): super().__init__( @@ -1783,7 +1769,7 @@ class OtherCog(commands.Cog): style=discord.InputTextStyle.long, ), title="Enter prompt", - timeout=120 + timeout=120, ) if is_owner: self.add_item( @@ -1846,7 +1832,7 @@ class OtherCog(commands.Cog): "codellama:python", "codellama:instruct", ], - "owner": 421698654189912064 + "owner": 421698654189912064, }, "ollama.shronk.net:11434": { "name": "Alibaba Cloud", @@ -1861,26 +1847,19 @@ class OtherCog(commands.Cog): "orca-mini:3b", "orca-mini:7b", ], - "owner": 421698654189912064 + "owner": 421698654189912064, }, } - H_DEFAULT = { - "name": "Other", - "allow": ["*"], - "owner": 1019217990111199243 - } + H_DEFAULT = {"name": "Other", "allow": ["*"], "owner": 1019217990111199243} def model_is_allowed(model_name: str, _srv: dict[str, str | list[str] | int]) -> bool: if _srv["owner"] == ctx.user.id: return True - for pat in _srv.get("allow", ['*']): + for pat in _srv.get("allow", ["*"]): if not fnmatch.fnmatch(model_name.lower(), pat.lower()): self.log.debug( - "Server %r does not support %r (only %r.)" % ( - _srv['name'], - model_name, - ', '.join(_srv['allow']) - ) + "Server %r does not support %r (only %r.)" + % (_srv["name"], model_name, ", ".join(_srv["allow"])) ) else: break @@ -1890,9 +1869,7 @@ class OtherCog(commands.Cog): class ServerSelector(discord.ui.View): def __init__(self): - super().__init__( - disable_on_timeout=True - ) + super().__init__(disable_on_timeout=True) self.chosen_server = None async def interaction_check(self, interaction: Interaction) -> bool: @@ -1902,21 +1879,15 @@ class OtherCog(commands.Cog): placeholder="Choose a server.", custom_id="select", options=[ - discord.SelectOption( - label="%s (%s)" % (y['name'], x), - value=x - ) + discord.SelectOption(label="%s (%s)" % (y["name"], x), value=x) for x, y in servers.items() if model_is_allowed(model, y) - ] + [ - discord.SelectOption( - label="Custom", - value="custom" - ) ] + + [discord.SelectOption(label="Custom", value="custom")], ) async def select_callback(self, item: discord.ui.Select, interaction: discord.Interaction): if item.values[0] == "custom": + class ServerSelectionModal(discord.ui.Modal): def __init__(self): super().__init__( @@ -1933,10 +1904,10 @@ class OtherCog(commands.Cog): min_length=2, max_length=5, style=discord.InputTextStyle.short, - value="11434" + value="11434", ), title="Enter server details", - timeout=120 + timeout=120, ) self.hostname = None self.port = None @@ -1955,8 +1926,7 @@ class OtherCog(commands.Cog): self.chosen_server = item.values[0] await interaction.response.defer(ephemeral=True) await interaction.followup.send( - f"\N{white heavy check mark} Selected server {self.chosen_server}/", - ephemeral=True + f"\N{white heavy check mark} Selected server {self.chosen_server}/", ephemeral=True ) self.stop() @@ -1974,29 +1944,18 @@ class OtherCog(commands.Cog): if not model_is_allowed(model, srv): return await ctx.respond( ":x: <@{!s}> does not allow you to run that model on the server {!r}. You can, however, use" - " any of the following: {}".format( - srv["owner"], - srv["name"], - ", ".join(srv.get("allow", ["*"])) - ) + " any of the following: {}".format(srv["owner"], srv["name"], ", ".join(srv.get("allow", ["*"]))) ) content = None - embed = discord.Embed( - colour=discord.Colour.greyple() - ) + embed = discord.Embed(colour=discord.Colour.greyple()) embed.set_author( name=f"Loading {model}", url=f"http://{host}", - icon_url="https://cdn.discordapp.com/emojis/1101463077586735174.gif" - ) - FOOTER_TEXT = "Powered by Ollama • Using server {} ({})".format( - host, - servers.get(host, H_DEFAULT)['name'] - ) - embed.set_footer( - text=FOOTER_TEXT + icon_url="https://cdn.discordapp.com/emojis/1101463077586735174.gif", ) + FOOTER_TEXT = "Powered by Ollama • Using server {} ({})".format(host, servers.get(host, H_DEFAULT)["name"]) + embed.set_footer(text=FOOTER_TEXT) msg = await ctx.respond(embed=embed, ephemeral=False) async with httpx.AsyncClient(follow_redirects=True) as client: @@ -2013,19 +1972,15 @@ class OtherCog(commands.Cog): error = "GET {0.url} HTTP {0.status_code}: {0.text}".format(e.response) return await msg.edit( embed=discord.Embed( - title="Failed to GET /tags. Offline?", - description=error, - colour=discord.Colour.red() + title="Failed to GET /tags. Offline?", description=error, colour=discord.Colour.red() ).set_footer(text=FOOTER_TEXT) ) except httpx.TransportError as e: return await msg.edit( embed=discord.Embed( title=f"Failed to connect to {host!r}", - description="Transport error sending request to {}: {}".format( - host, str(e) - ), - colour=discord.Colour.red() + description="Transport error sending request to {}: {}".format(host, str(e)), + colour=discord.Colour.red(), ).set_footer(text=FOOTER_TEXT) ) # get models @@ -2033,9 +1988,7 @@ class OtherCog(commands.Cog): response = await client.post("/show", json={"name": model}) except httpx.TransportError as e: embed = discord.Embed( - title="Failed to connect to Ollama.", - description=str(e), - colour=discord.Colour.red() + title="Failed to connect to Ollama.", description=str(e), colour=discord.Colour.red() ) embed.set_footer(text=FOOTER_TEXT) return await msg.edit(embed=embed) @@ -2044,10 +1997,7 @@ class OtherCog(commands.Cog): await msg.edit(embed=embed) async with ctx.channel.typing(): async with client.stream( - "POST", - "/pull", - json={"name": model, "stream": True}, - timeout=None + "POST", "/pull", json={"name": model, "stream": True}, timeout=None ) as response: if response.status_code != 200: error = await response.aread() @@ -2055,7 +2005,7 @@ class OtherCog(commands.Cog): title=f"Failed to download model {model}:", description=f"HTTP {response.status_code}:\n```{error or ''}\n```", colour=discord.Colour.red(), - url=str(response.url) + url=str(response.url), ) embed.set_footer(text=FOOTER_TEXT) return await msg.edit(embed=embed) @@ -2070,12 +2020,13 @@ class OtherCog(commands.Cog): percent = round(completed / total * 100, 2) total_gigabytes = total / 1024 / 1024 / 1024 completed_gigabytes = completed / 1024 / 1024 / 1024 - lines[chunk["status"]] = (f"{percent}% " - f"({completed_gigabytes:.2f}GB/{total_gigabytes:.2f}GB)") + lines[chunk["status"]] = ( + f"{percent}% " f"({completed_gigabytes:.2f}GB/{total_gigabytes:.2f}GB)" + ) else: status = chunk.get("status", chunk.get("error", os.urandom(3).hex())) lines[status] = status - + embed.description = "\n".join(f"`{k}`: {v}" for k, v in lines.items()) if (time() - last_edit) >= 5: await msg.edit(embed=embed) @@ -2090,7 +2041,7 @@ class OtherCog(commands.Cog): title=f"Failed to download model {model}:", description=f"HTTP {response.status_code}:\n```{error or ''}\n```", colour=discord.Colour.red(), - url=str(response.url) + url=str(response.url), ) embed.set_footer(text=FOOTER_TEXT) return await msg.edit(embed=embed) @@ -2099,32 +2050,21 @@ class OtherCog(commands.Cog): title=f"{model} says:", description="", colour=discord.Colour.blurple(), - timestamp=discord.utils.utcnow() + timestamp=discord.utils.utcnow(), ) embed.set_footer(text=FOOTER_TEXT) await msg.edit(embed=embed) async with ctx.channel.typing(): - payload = { - "model": model, - "prompt": query, - "format": "json", - "system": system_prompt, - "stream": True - } + payload = {"model": model, "prompt": query, "format": "json", "system": system_prompt, "stream": True} if context: payload["context"] = context - async with client.stream( - "POST", - "/generate", - json=payload, - timeout=None - ) as response: + async with client.stream("POST", "/generate", json=payload, timeout=None) as response: if response.status_code != 200: error = await response.aread() embed = discord.Embed( title=f"Failed to generate response from {model}:", description=f"HTTP {response.status_code}:\n```{error or ''}\n```", - colour=discord.Colour.red() + colour=discord.Colour.red(), ) embed.set_footer(text=FOOTER_TEXT) return await msg.edit(embed=embed) @@ -2143,7 +2083,7 @@ class OtherCog(commands.Cog): embed.set_author( name=f"Generating response with {model}", url=f"http://{host}", - icon_url="https://cdn.discordapp.com/emojis/1101463077586735174.gif" + icon_url="https://cdn.discordapp.com/emojis/1101463077586735174.gif", ) embed.description += chunk["response"] if (time() - last_edit) >= 5 or chunk["done"] is True: @@ -2154,10 +2094,7 @@ class OtherCog(commands.Cog): embed.colour = discord.Colour.red() return await msg.edit(embed=embed, view=None) if len(embed.description) >= 4000: - embed.add_field( - name="Aborting early", - value="Output exceeded 4000 characters." - ) + embed.add_field(name="Aborting early", value="Output exceeded 4000 characters.") embed.title = embed.title[:-1] + " (Aborted)" embed.colour = discord.Colour.red() embed.description = embed.description[:4096] @@ -2207,28 +2144,22 @@ class OtherCog(commands.Cog): self.context_cache[key] = context else: context = key = None - value = ("* Total: {}\n" - "* Model load: {}\n" - "* Sample generation: {}\n" - "* Prompt eval: {}\n" - "* Response generation: {}\n").format( + value = ( + "* Total: {}\n" + "* Model load: {}\n" + "* Sample generation: {}\n" + "* Prompt eval: {}\n" + "* Response generation: {}\n" + ).format( total_time_spent, load_time_spent, sample_time_sent, prompt_eval_time_spent, eval_time_spent, ) - embed.add_field( - name="Timings", - value=value, - inline=False - ) + embed.add_field(name="Timings", value=value, inline=False) if context: - embed.add_field( - name="Context Key", - value=key, - inline=True - ) + embed.add_field(name="Context Key", value=key, inline=True) embed.set_footer(text=FOOTER_TEXT) await msg.edit(content=None, embed=embed, view=None) self.ollama_locks.pop(msg, None) @@ -2236,44 +2167,31 @@ class OtherCog(commands.Cog): @commands.slash_command(name="test-proxies") @commands.max_concurrency(1, commands.BucketType.user, wait=False) async def test_proxy( - self, - ctx: discord.ApplicationContext, - run_speed_test: bool = False, - proxy_name: discord.Option( - str, - choices=[ - "SHRoNK", - "NexBox", - "first-working" - ] - ) = "first-working", - test_time: float = 30 + self, + ctx: discord.ApplicationContext, + run_speed_test: bool = False, + proxy_name: discord.Option(str, choices=["SHRoNK", "NexBox", "first-working"]) = "first-working", + test_time: float = 30, ): """Tests proxies.""" test_time = max(5.0, test_time) await ctx.defer() - SPEED_REGIONS = [ - "fsn1", - "nbg1", - "hel1", - "ash", - "hil" - ] + SPEED_REGIONS = ["fsn1", "nbg1", "hel1", "ash", "hil"] results = { "localhost:1090": { "name": "SHRoNK", "failure": None, "download_speed": 0.0, "tested": False, - "speedtest": "https://{hetzner_region}-speed.hetzner.com/100M.bin" + "speedtest": "https://{hetzner_region}-speed.hetzner.com/100M.bin", }, "localhost:1080": { "name": "NexBox", "failure": None, "download_speed": 0.0, "tested": False, - "speedtest": "http://192.168.0.90:82/100M.bin" - } + "speedtest": "http://192.168.0.90:82/100M.bin", + }, } if proxy_name != "first-working": for key, value in results.copy().items(): @@ -2281,18 +2199,13 @@ class OtherCog(commands.Cog): continue else: results.pop(key) - embed = discord.Embed( - title="\N{white heavy check mark} Proxy available." - ) + embed = discord.Embed(title="\N{white heavy check mark} Proxy available.") FAILED = False proxy_uri = None for proxy_uri in results.keys(): name = results[proxy_uri]["name"] try: - proxy_down = await asyncio.wait_for( - self.check_proxy("socks5://" + proxy_uri), - timeout=10 - ) + proxy_down = await asyncio.wait_for(self.check_proxy("socks5://" + proxy_uri), timeout=10) results[proxy_uri]["tested"] = True if proxy_down > 0: embed.colour = discord.Colour.red() @@ -2314,19 +2227,12 @@ class OtherCog(commands.Cog): results[proxy_uri]["failure"] = f"Failed to check {name} proxy (`{e}`)." results[proxy_uri]["tested"] = True else: - embed = discord.Embed( - title="\N{cross mark} All proxies failed.", - colour=discord.Colour.red() - ) + embed = discord.Embed(title="\N{cross mark} All proxies failed.", colour=discord.Colour.red()) FAILED = True for uri, value in results.items(): if value["tested"]: - embed.add_field( - name=value["name"], - value=value["failure"] or "Proxy is working.", - inline=True - ) + embed.add_field(name=value["name"], value=value["failure"] or "Proxy is working.", inline=True) embed.set_footer(text="No speed test will be run.") await ctx.respond(embed=embed) if run_speed_test and FAILED is False: @@ -2335,11 +2241,9 @@ class OtherCog(commands.Cog): await ctx.edit(embed=embed) chosen_proxy = ("socks5://" + proxy_uri) if proxy_uri else None async with httpx.AsyncClient( - http2=True, - proxies=chosen_proxy, - headers={ - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0" - } + http2=True, + proxies=chosen_proxy, + headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0"}, ) as client: bytes_received = 0 for region in SPEED_REGIONS: @@ -2357,9 +2261,7 @@ class OtherCog(commands.Cog): response.raise_for_status() end = time() now = discord.utils.utcnow() - embed.set_footer( - text=embed.footer.text + " | Finished at: " + now.strftime("%X") - ) + embed.set_footer(text=embed.footer.text + " | Finished at: " + now.strftime("%X")) break except Exception as e: results[proxy_uri]["failure"] = f"Failed to test {region} speed (`{e}`)." @@ -2372,8 +2274,8 @@ class OtherCog(commands.Cog): embed2 = discord.Embed( title=f"\U000023f2\U0000fe0f Speed test results (for {proxy_uri})", description=f"Downloaded {megabytes:,.1f}MB in {elapsed:,.0f} seconds " - f"({megabits_per_second:,.0f}Mbps).\n`{latency:,.0f}ms` latency.", - colour=discord.Colour.green() if megabits_per_second >= 50 else discord.Colour.red() + f"({megabits_per_second:,.0f}Mbps).\n`{latency:,.0f}ms` latency.", + colour=discord.Colour.green() if megabits_per_second >= 50 else discord.Colour.red(), ) embed2.add_field(name="Source", value=used) await ctx.edit(embeds=[embed, embed2]) @@ -2407,15 +2309,11 @@ class OtherCog(commands.Cog): f2.seek(0) seg: pydub.AudioSegment = await asyncio.to_thread(pydub.AudioSegment.from_file, file=f2, format=_ft) seg = seg.set_channels(1) - await asyncio.to_thread( - seg.export, f.name, format="mp4" - ) + await asyncio.to_thread(seg.export, f.name, format="mp4") f.seek(0) transcript = await asyncio.to_thread( - client.audio.transcriptions.create, - file=pathlib.Path(f.name), - model="whisper-1" + client.audio.transcriptions.create, file=pathlib.Path(f.name), model="whisper-1" ) text = transcript.text cache.write_text(text) @@ -2428,15 +2326,51 @@ class OtherCog(commands.Cog): paginator.add_line(textwrap.shorten(line, 4096)) embeds = list(map(lambda p: discord.Embed(description=p), paginator.pages)) await ctx.respond(embeds=embeds or [discord.Embed(description="No text found.")]) - + if await self.bot.is_owner(ctx.user): await ctx.respond( - ("Cached response ({})" if cached else "Uncached response ({})").format( - file_hash - ), - ephemeral=True + ("Cached response ({})" if cached else "Uncached response ({})").format(file_hash), ephemeral=True ) + @commands.slash_command() + async def whois(self, ctx: discord.ApplicationContext, domain: str): + """Runs a WHOIS.""" + await ctx.defer() + url = urlparse("http://" + domain) + if not url.hostname: + return await ctx.respond("Invalid domain.") + + process = await asyncio.create_subprocess_exec( + "whois", "-H", url.hostname, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, text=True + ) + stdout, stderr = await process.communicate() + await process.wait() + if process.returncode != 0: + return await ctx.respond(f"Error:\n```{stderr[:3900]}```") + + paginator = commands.Paginator() + redacted = io.BytesIO() + for line in stdout.splitlines(): + if line.startswith(">>> Last update"): + break + if "REDACTED" in line: + redacted.write(line.encode() + b"\n") + else: + paginator.add_line(line) + + if redacted.tell() > 0: + redacted.seek(0) + file = discord.File(redacted, "redacted-fields.txt", description="Any discovered redacted fields.") + else: + file = None + if len(paginator.pages) > 1: + for page in paginator.pages: + await ctx.respond(page) + if file: + await ctx.respond(file=file) + else: + await ctx.respond(paginator.pages[0], file=file) + def setup(bot): bot.add_cog(OtherCog(bot)) diff --git a/cogs/timetable.py b/cogs/timetable.py index 41738e8..2fb4f99 100644 --- a/cogs/timetable.py +++ b/cogs/timetable.py @@ -5,10 +5,10 @@ from datetime import datetime, time, timedelta, timezone from pathlib import Path from typing import Dict, Optional, Union -import config import discord from discord.ext import commands, tasks +import config from utils import TimeTableDaySwitcherView, console diff --git a/main.py b/main.py index d7a83ab..e61efe7 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,15 @@ +import logging import os import signal import sys -import logging import textwrap from datetime import datetime, timedelta, timezone + +import discord +from discord.ext import commands from rich.logging import RichHandler import config -import discord -from discord.ext import commands - from utils import JimmyBanException, JimmyBans, console, get_or_none from utils.client import bot @@ -25,13 +25,10 @@ logging.basicConfig( markup=True, rich_tracebacks=True, show_path=False, - show_time=False + show_time=False, ), - logging.FileHandler( - "jimmy.log", - "a" - ) - ] + logging.FileHandler("jimmy.log", "a"), + ], ) logging.getLogger("discord.gateway").setLevel(logging.WARNING) for _ln in [ @@ -43,7 +40,7 @@ for _ln in [ "discord.bot", "httpcore.http11", "aiosqlite", - "httpx" + "httpx", ]: logging.getLogger(_ln).setLevel(logging.INFO) diff --git a/utils/client.py b/utils/client.py index 419d724..b7b9c12 100644 --- a/utils/client.py +++ b/utils/client.py @@ -6,10 +6,11 @@ from datetime import datetime, timezone from pathlib import Path from typing import TYPE_CHECKING, Dict, Optional, Union -import config import discord from discord.ext import commands +import config + if TYPE_CHECKING: from asyncio import Task @@ -57,6 +58,7 @@ class Bot(commands.Bot): log.info(f"Loaded extension [green]{ext}") if getattr(config, "CONNECT_MODE", None) == 2: + async def connect(self, *, reconnect: bool = True) -> None: self.log.critical("Exit target 2 reached, shutting down (not connecting to discord).") return diff --git a/utils/views.py b/utils/views.py index d101db0..67da8c2 100644 --- a/utils/views.py +++ b/utils/views.py @@ -8,13 +8,8 @@ import discord import orm from discord.ui import View -from utils import ( - BannedStudentID, - Student, - VerifyCode, - console, - get_or_none, -) +from utils import BannedStudentID, Student, VerifyCode, console, get_or_none + TOKEN_LENGTH = 16 if typing.TYPE_CHECKING: diff --git a/web/server.py b/web/server.py index f6ee28e..ff349d9 100644 --- a/web/server.py +++ b/web/server.py @@ -3,6 +3,7 @@ import ipaddress import logging import os import textwrap +from asyncio import Lock from datetime import datetime, timezone from hashlib import sha512 from http import HTTPStatus @@ -10,13 +11,13 @@ from pathlib import Path import discord import httpx -from config import guilds -from asyncio import Lock -from fastapi import FastAPI, Header, HTTPException, Request, WebSocketException as _WSException -from websockets.exceptions import WebSocketException +from fastapi import FastAPI, Header, HTTPException, Request +from fastapi import WebSocketException as _WSException from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from starlette.websockets import WebSocket, WebSocketDisconnect +from websockets.exceptions import WebSocketException +from config import guilds from utils import BannedStudentID, Student, VerifyCode, console, get_or_none from utils.db import AccessTokens @@ -112,7 +113,8 @@ async def authenticate(req: Request, code: str = None, state: str = None): return RedirectResponse( discord.utils.oauth_url( OAUTH_ID, redirect_uri=OAUTH_REDIRECT_URI, scopes=("identify", "connections", "guilds", "email") - ) + f"&state={value}&prompt=none", + ) + + f"&state={value}&prompt=none", status_code=HTTPStatus.TEMPORARY_REDIRECT, headers={"Cache-Control": "no-store, no-cache"}, )