From 0f631e003db3d0acf4ee5056e4403358f07fa2a5 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Tue, 25 Jun 2024 23:43:49 +0100 Subject: [PATCH 01/13] Alter the penis algorithm --- src/cogs/gay_meter.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/cogs/gay_meter.py b/src/cogs/gay_meter.py index 96828f1..6c73923 100644 --- a/src/cogs/gay_meter.py +++ b/src/cogs/gay_meter.py @@ -13,6 +13,7 @@ class MeterCog(commands.Cog): self.cache = {} @commands.slash_command(name="gay-meter") + @discord.guild_only() async def gay_meter(self, ctx: discord.ApplicationContext, user: discord.User = None): """Checks how gay someone is""" user = user or ctx.user @@ -21,23 +22,34 @@ class MeterCog(commands.Cog): for i in range(0, 125, 25): await ctx.edit(content="Calculating... %d%%" % i) await asyncio.sleep(random.randint(1, 30) / 10) - - pct = user.id % 100 + + if random.randint(0, 1): + pct = sum((user.id, ctx.user.id, ctx.channel.id, ctx.guild.id)) % 400 + else: + pct = user.id % 100 await ctx.edit(content=f"{user.mention} is {pct}% gay.") @commands.slash_command(name="penis-length") async def penis_meter(self, ctx: discord.ApplicationContext, user: discord.User = None): """Checks the length of someone's penis.""" user = user or ctx.user - n = self.cache.get(user) or random.randint(0, 100) + if random.randint(0, 1): + pct = sum((user.id, ctx.user.id)) % 200 + else: + pct = user.id % 125 + pct = pct chunks = ["8"] - chunks += ["="] * n + chunks += ["="] * pct chunks.append("B") - self.cache[user] = n + inch = pct * 0.3937008 + im = "inch" + if pct > 30.48: + im = "ft" + inch = pct * 0.0328084 return await ctx.respond( embed=discord.Embed( title=f"{user.display_name}'s penis length:", - description="%d cm\n%s" % (n, "".join(chunks)) + description="%d cm (%.2f%s)\n%s" % (pct, inch, im, "".join(chunks)) ) ) From e39134719a306bcb5da08f7fbd1f697b8f05734d Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 27 Jun 2024 00:35:49 +0100 Subject: [PATCH 02/13] Allow abort failover to CPU-based instances --- config.example.toml | 1 + src/cogs/ollama.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/config.example.toml b/config.example.toml index 89e5f59..d889fab 100644 --- a/config.example.toml +++ b/config.example.toml @@ -34,6 +34,7 @@ allowed_models = [ # Note that every model has a tag called "latest" which is the most recent version. ] base_url = "http://ollama:11434/api" # this is the default if you're running via docker compose +is_gpu = false [ollama.external] owner = 421698654189912064 diff --git a/src/cogs/ollama.py b/src/cogs/ollama.py index 58f21fe..7fb5804 100644 --- a/src/cogs/ollama.py +++ b/src/cogs/ollama.py @@ -461,6 +461,28 @@ class PromptSelector(discord.ui.View): @discord.ui.button(label="Done", style=discord.ButtonStyle.success, custom_id="done") async def done(self, btn: discord.ui.Button, interaction: Interaction): + self.ctx.interaction = interaction + self.stop() + + +class ConfirmCPURun(discord.ui.View): + def __init__(self, ctx: discord.ApplicationContext): + super().__init__(timeout=600, disable_on_timeout=True) + self.ctx = ctx + self.proceed = False + + async def interaction_check(self, interaction: Interaction) -> bool: + return interaction.user == self.ctx.user + + @discord.ui.button(label="Run on CPU", style=discord.ButtonStyle.primary, custom_id="cpu") + async def run_on_cpu(self, btn: discord.ui.Button, interaction: Interaction): + await interaction.response.defer(invisible=True) + self.proceed = True + self.stop() + + @discord.ui.button(label="Abort", style=discord.ButtonStyle.primary, custom_id="gpu") + async def run_on_gpu(self, btn: discord.ui.Button, interaction: Interaction): + await interaction.response.defer(invisible=True) self.stop() @@ -637,6 +659,24 @@ class Ollama(commands.Cog): for i in range(10): try: server = self.next_server(tried) + if server and CONFIG["ollama"]["server"].get("is_gpu", False) is not True: + cf = ConfirmCPURun(ctx) + await ctx.edit( + embed=discord.Embed( + title=f"Server {server} is available, but...", + description="It is CPU only, which means it is very slow and will likely crash.\n" + "If you really want, you can continue your generation, using this " + "server. Be aware though, once in motion, it cannot be stopped.\n\n" + "" + "Continue?", + color=discord.Color.red(), + ), + view=cf + ) + await cf.wait() + await ctx.edit(view=None) + if cf.proceed: + break except RuntimeError: tried.add(server) continue From f092e4db480a9b4bffa3a9fdb544902fa0347895 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 27 Jun 2024 00:50:21 +0100 Subject: [PATCH 03/13] Add second config check --- src/cogs/ollama.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/cogs/ollama.py b/src/cogs/ollama.py index 7fb5804..5223256 100644 --- a/src/cogs/ollama.py +++ b/src/cogs/ollama.py @@ -792,6 +792,27 @@ class Ollama(commands.Cog): key = os.urandom(6).hex() + if server_config.get("is_gpu", False) is False: + cf2 = ConfirmCPURun(ctx) + await ctx.edit( + content=None, + embed=discord.Embed( + title="Before you continue", + description="You've selected a CPU-only server. This will be really slow. This will also likely" + " bring the host to a halt. Consider the pain the CPU is about to endure. " + "Are you super" + " sure you want to continue? You can run `h!ollama-status` to see what servers are" + " available.", + color=discord.Color.red(), + ), + view=cf2 + ) + await cf2.wait() + if cf2.proceed is False: + return await ctx.edit("Cancelled.", embed=None, view=None) + else: + await ctx.edit(view=None) + embed = discord.Embed( title="Generating response...", description=">>> ", From 9d199f0102e28e686cd50462632ea92f7ac150a3 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 18:28:06 +0100 Subject: [PATCH 04/13] Add nmap command --- requirements.txt | 1 + src/cogs/net.py | 130 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/requirements.txt b/requirements.txt index 78aca47..073051b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,3 +20,4 @@ python-magic~=0.4 aiofiles~=23.2 fuzzywuzzy[speedup]~=0.18 tortoise-orm[asyncpg]~=0.21 +superpaste @ git+https://github.com/nexy7574/superpaste.git@e31eca6 diff --git a/src/cogs/net.py b/src/cogs/net.py index 7755d44..cb1ef66 100644 --- a/src/cogs/net.py +++ b/src/cogs/net.py @@ -3,8 +3,12 @@ import io import json import os import re +import shlex +import shutil +import tempfile import time import typing +import warnings from pathlib import Path import discord @@ -14,6 +18,8 @@ from dns import asyncresolver from rich.console import Console from rich.tree import Tree from conf import CONFIG +from superpaste import HstSHBackend +from superpaste.backends import GenericFile class GetFilteredTextView(discord.ui.View): @@ -346,6 +352,130 @@ class NetworkCog(commands.Cog): await ctx.respond(embed=embed) + @commands.slash_command() + @commands.max_concurrency(1, commands.BucketType.user) + async def nmap( + self, + ctx: discord.ApplicationContext, + target: str, + technique: typing.Annotated[ + str, + discord.Option( + str, + choices=[ + discord.OptionChoice(name="TCP SYN", value="S"), + discord.OptionChoice(name="TCP Connect", value="T"), + discord.OptionChoice(name="TCP ACK", value="A"), + discord.OptionChoice(name="TCP Window", value="W"), + discord.OptionChoice(name="TCP Maimon", value="M"), + discord.OptionChoice(name="UDP", value="U"), + discord.OptionChoice(name="TCP Null", value="N"), + discord.OptionChoice(name="TCP FIN", value="F"), + discord.OptionChoice(name="TCP XMAS", value="X"), + ], + default="sT" + ) + ], + treat_all_hosts_online: bool = False, + service_scan: bool = False, + fast_mode: bool = False, + enable_os_detection: bool = False, + timing: typing.Annotated[ + int, + discord.Option( + int, + description="Timing template to use 0 is slowest, 5 is fastest.", + choices=[0, 1, 2, 3, 4, 5], + default=3 + ) + ] = 3, + ports: str = None + ): + """Runs nmap on a target. You cannot specify multiple targets.""" + await ctx.defer() + if len(shlex.split(target)) > 1: + return await ctx.respond("You cannot specify multiple targets.") + if not shutil.which("nmap"): + warnings.warn( + "NMAP is not installed on this system, so the /nmap command is not enabled. " + "If you would like to enable it, install nmap, or mount the binary as a volume in docker." + ) + return await ctx.respond("Nmap is not installed on this system.") + + is_superuser = os.getuid() == 0 + if technique != "T" and not is_superuser: + return await ctx.respond("Only `TCP Connect` can be used on this system.") + if enable_os_detection and not is_superuser: + return await ctx.respond("OS detection is not available on this system.") + + with tempfile.TemporaryDirectory(prefix=f"nmap-{ctx.user.id}-{ctx.message.created_at.timestamp():.0f}") as tmp: + tmp_dir = Path(tmp) + args = [ + "nmap", + "-oA", + str(tmp_dir.resolve() / target), + "-T", + str(timing), + "-s" + technique, + ] + if treat_all_hosts_online: + args.append("-Pn") + if service_scan: + args.append("-sV") + if fast_mode: + args.append("-F") + if ports: + args.extend(("-p", ports)) + if enable_os_detection: + args.append("-O") + args.append(target) + + await ctx.respond( + embed=discord.Embed( + title="Running nmap...", + description="Command:\n" + "```{}```".format(shlex.join(args)), + ) + ) + process = await asyncio.create_subprocess_exec( + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + _, stderr = await process.communicate() + files = [discord.File(x, filename=x.name + ".txt") for x in tmp_dir.iterdir()] + if not files: + if len(stderr) <= 4089: + return await ctx.edit( + embed=discord.Embed( + title="Nmap failed.", + description="```\n" + stderr.decode() + "```", + color=discord.Color.red() + ) + ) + + result = await HstSHBackend().async_create_paste( + GenericFile(stderr.decode()) + ) + return await ctx.edit( + embed=discord.Embed( + title="Nmap failed.", + description=f"Output was too long. [View full output]({result.url})", + color=discord.Color.red() + ) + ) + await ctx.edit( + embed=discord.Embed( + title="Nmap finished!", + description="Result files are attached.\n" + "* `gnmap` is 'greppable'\n" + "* `xml` is XML output\n" + "* `nmap` is normal output", + color=discord.Color.green() + ), + files=files + ) + def setup(bot): bot.add_cog(NetworkCog(bot)) From d46ef03ba1d183a58c2552036ddd15c900fe7468 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 18:50:22 +0100 Subject: [PATCH 05/13] Strip down docker container size --- Dockerfile | 95 ++++++++++++++++++-------------------- docker-compose.yml | 5 ++ src/cogs/auto_responder.py | 22 +++++++-- src/cogs/election.py | 1 - src/cogs/ffmeta.py | 14 ++++++ src/cogs/net.py | 12 +++++ src/cogs/ytdl.py | 8 +++- 7 files changed, 101 insertions(+), 56 deletions(-) diff --git a/Dockerfile b/Dockerfile index 270e844..22c0378 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.12 LABEL org.opencontainers.image.source https://git.i-am.nexus/nex/college-bot-v2 LABEL org.opencontainers.image.url https://git.i-am.nexus/nex/college-bot-v2 @@ -11,50 +11,50 @@ WORKDIR /app RUN DEBIAN_FRONTEND=noninteractive apt-get update # Install chrome dependencies -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ -webext-ublock-origin-chromium \ -gconf-service \ -libasound2 \ -libatk1.0-0 \ -libc6 \ -libcairo2 \ -libcups2 \ -libdbus-1-3 \ -libexpat1 \ -libfontconfig1 \ -libgcc1 \ -libgconf-2-4 \ -libgdk-pixbuf2.0-0 \ -libglib2.0-0 \ -libgtk-3-0 \ -libnspr4 \ -libpango-1.0-0 \ -libpangocairo-1.0-0 \ -libstdc++6 \ -libx11-6 \ -libx11-xcb1 \ -libxcb1 \ -libxcomposite1 \ -libxcursor1 \ -libxdamage1 \ -libxext6 \ -libxfixes3 \ -libxi6 \ -libxrandr2 \ -libxrender1 \ -libxss1 \ -libxtst6 \ -ca-certificates \ -fonts-liberation \ -libappindicator1 \ -libnss3 \ -lsb-release \ -xdg-utils - -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - chromium-common \ - chromium-driver \ - chromium-sandbox +#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ +#webext-ublock-origin-chromium \ +#gconf-service \ +#libasound2 \ +#libatk1.0-0 \ +#libc6 \ +#libcairo2 \ +#libcups2 \ +#libdbus-1-3 \ +#libexpat1 \ +#libfontconfig1 \ +#libgcc1 \ +#libgconf-2-4 \ +#libgdk-pixbuf2.0-0 \ +#libglib2.0-0 \ +#libgtk-3-0 \ +#libnspr4 \ +#libpango-1.0-0 \ +#libpangocairo-1.0-0 \ +#libstdc++6 \ +#libx11-6 \ +#libx11-xcb1 \ +#libxcb1 \ +#libxcomposite1 \ +#libxcursor1 \ +#libxdamage1 \ +#libxext6 \ +#libxfixes3 \ +#libxi6 \ +#libxrandr2 \ +#libxrender1 \ +#libxss1 \ +#libxtst6 \ +#ca-certificates \ +#fonts-liberation \ +#libappindicator1 \ +#libnss3 \ +#lsb-release \ +#xdg-utils +# +#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ +# chromium-common \ +# chromium-driver \ +# chromium-sandbox # Install general utilities RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ @@ -67,11 +67,6 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ curl \ libmagic-dev -# Install image dependencies -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - ffmpeg \ - imagemagick - RUN python3 -m venv /app/venv RUN /app/venv/bin/pip install --upgrade --no-input pip wheel setuptools diff --git a/docker-compose.yml b/docker-compose.yml index 4a6619c..c8adbb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,11 @@ services: - ./jimmy.log:/app/jimmy.log - /dev/dri:/dev/dri - jimmy-data:/app/data + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /usr/bin/nmap:/usr/bin/nmap:ro + - /usr/bin/ffmpeg:/usr/bin/ffmpeg:ro + - /usr/bin/ffprobe:/usr/bin/ffprobe:ro extra_hosts: - host.docker.internal:host-gateway depends_on: diff --git a/src/cogs/auto_responder.py b/src/cogs/auto_responder.py index 44dd2b4..03f8329 100644 --- a/src/cogs/auto_responder.py +++ b/src/cogs/auto_responder.py @@ -1,5 +1,11 @@ +# In order to use commands in this system, the following things must be installed (ideally in /usr/local/bin): +# - `ffmpeg`, usually `ffmpeg`, sometimes `ffmpeg-full`. +# - /dev/dri should be passed through to the container for hardware acceleration. +# - `ffprobe`, usually `ffmpeg`, sometimes `ffmpeg-full`. +# In docker, you can just bind these as volumes, and read-only too. import asyncio import io +import shutil import typing from collections.abc import Iterable import logging @@ -105,6 +111,12 @@ class AutoResponder(commands.Cog): :param uri: The URI to transcode :return: A transcoded file """ + if not shutil.which("ffmpeg"): + self.log.error("ffmpeg not installed") + return + if not shutil.which("ffprobe"): + self.log.error("ffprobe not installed") + return last_reaction: str | None = None def update_reaction(new: str | None = None) -> None: @@ -118,9 +130,7 @@ class AutoResponder(commands.Cog): last_reaction = new self.log.info("Waiting for transcode lock to release") async with self.transcode_lock: - cog: FFMeta = self.bot.get_cog("FFMeta") - if not cog: - raise RuntimeError("FFMeta cog not loaded") + cog = FFMeta(self.bot) if not isinstance(uri, str): uri = str(uri) @@ -240,6 +250,12 @@ class AutoResponder(commands.Cog): *domains: str, additional: Iterable[str] = None ) -> None: + if not shutil.which("ffmpeg"): + self.log.error("ffmpeg not installed") + return + if not shutil.which("ffprobe"): + self.log.error("ffprobe not installed") + return if self.allow_hevc_to_h264 is False: self.log.debug("hevc_to_h264 is disabled, not engaging.") return diff --git a/src/cogs/election.py b/src/cogs/election.py index b490787..7dac3be 100644 --- a/src/cogs/election.py +++ b/src/cogs/election.py @@ -2,7 +2,6 @@ This module is only meant to be loaded during election times. """ import asyncio -import datetime import logging import re diff --git a/src/cogs/ffmeta.py b/src/cogs/ffmeta.py index ef69879..06bda53 100644 --- a/src/cogs/ffmeta.py +++ b/src/cogs/ffmeta.py @@ -1,8 +1,14 @@ +# In order to use commands in this system, the following things must be installed (ideally in /usr/local/bin): +# - `ffmpeg`, usually `ffmpeg`, sometimes `ffmpeg-full`. +# - /dev/dri should be passed through to the container for hardware acceleration. +# - `ffprobe`, usually `ffmpeg`, sometimes `ffmpeg-full`. +# In docker, you can just bind these as volumes, and read-only too. import asyncio import io import json import logging import pathlib +import shutil import sys import tempfile import typing @@ -56,6 +62,8 @@ class FFMeta(commands.Cog): @commands.slash_command() async def ffprobe(self, ctx: discord.ApplicationContext, url: str = None, attachment: discord.Attachment = None): """Runs ffprobe on a given URL or attachment""" + if not shutil.which("ffprobe"): + return await ctx.respond("ffprobe is not installed on this system.") if url is None: if attachment is None: return await ctx.respond("No URL or attachment provided") @@ -142,6 +150,10 @@ class FFMeta(commands.Cog): ] = False, ): """Converts a given URL or attachment to an Opus file""" + if not shutil.which("ffmpeg"): + return await ctx.respond("ffmpeg is not installed on this system.") + if not shutil.which("ffprobe"): + return await ctx.respond("ffprobe is not installed on this system.") if bitrate == 0: bitrate = 0.5 if mono: @@ -244,6 +256,8 @@ class FFMeta(commands.Cog): rbh = Path("assets/right-behind-you.ogg").resolve() if not rbh.exists(): return await ctx.respond("The file `right-behind-you.ogg` is missing.") + if not shutil.which("ffmpeg"): + return await ctx.respond("ffmpeg is not installed on this system.") if not image.content_type.startswith("image"): return await ctx.respond("That's not an image!") with tempfile.NamedTemporaryFile(suffix=Path(image.filename).suffix) as temp: diff --git a/src/cogs/net.py b/src/cogs/net.py index cb1ef66..95c1c22 100644 --- a/src/cogs/net.py +++ b/src/cogs/net.py @@ -1,3 +1,9 @@ +# In order to use commands in this system, the following things must be installed (ideally in /usr/local/bin): +# - `ping`, usually iputils-ping +# - `whois`, usually whois +# - `traceroute`, usually traceroute +# - `nmap`, usually nmap +# In docker, you can just bind these as volumes, and read-only too. import asyncio import io import json @@ -49,6 +55,8 @@ class NetworkCog(commands.Cog): @commands.slash_command() async def ping(self, ctx: discord.ApplicationContext, target: str = None): """Get the bot's latency, or the network latency to a target.""" + if not shutil.which("ping"): + return await ctx.respond("Ping is not installed on this system.") if target is None: return await ctx.respond(f"Pong! {round(self.bot.latency * 1000)}ms") else: @@ -75,6 +83,8 @@ class NetworkCog(commands.Cog): @commands.slash_command() async def whois(self, ctx: discord.ApplicationContext, target: str): """Get information about a user.""" + if not shutil.which("whois"): + return await ctx.respond("Whois is not installed on this system.") async def run_command(with_disclaimer: bool = False): args = [] if with_disclaimer else ["-H"] @@ -221,6 +231,8 @@ class NetworkCog(commands.Cog): await ctx.defer() if re.search(r"\s+", url): return await ctx.respond("URL cannot contain spaces.") + if not shutil.which("traceroute"): + return await ctx.respond("Traceroute is not installed on this system.") args = ["sudo", "-E", "-n", "traceroute"] flags = { diff --git a/src/cogs/ytdl.py b/src/cogs/ytdl.py index c3023c5..833b1f2 100644 --- a/src/cogs/ytdl.py +++ b/src/cogs/ytdl.py @@ -3,6 +3,7 @@ import functools import hashlib import logging import math +import shutil import time import datetime @@ -190,6 +191,8 @@ class YTDLCog(commands.Cog): :param file: The file to convert :return: The converted file """ + if not shutil.which("ffmpeg"): + raise RuntimeError("ffmpeg is not installed.") new_file = file.with_suffix(".m4a") args = [ "-vn", @@ -319,8 +322,9 @@ class YTDLCog(commands.Cog): else: chosen_format = user_format + ffmpeg_installed = bool(shutil.which("ffmpeg")) options.setdefault("postprocessors", []) - if audio_only: + if audio_only and ffmpeg_installed: # Overwrite format here to be best audio under 25 megabytes. chosen_format = "ba[filesize<20M]" # Also force sorting by the best audio bitrate first. @@ -332,7 +336,7 @@ class YTDLCog(commands.Cog): options["format"] = chosen_format options["paths"] = paths - if subtitles: + if subtitles and ffmpeg_installed: subtitles, burn = subtitles.split("+", 1) if "+" in subtitles else (subtitles, "0") burn = burn[0].lower() in ("y", "1", "t") if subtitles.lower() == "auto": From 0d470fb85710cacf8e4a3e978271ef62944b572c Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 18:53:19 +0100 Subject: [PATCH 06/13] ctx.message -> discord.utils.utcnow() --- src/cogs/net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/net.py b/src/cogs/net.py index 95c1c22..91bab30 100644 --- a/src/cogs/net.py +++ b/src/cogs/net.py @@ -420,7 +420,7 @@ class NetworkCog(commands.Cog): if enable_os_detection and not is_superuser: return await ctx.respond("OS detection is not available on this system.") - with tempfile.TemporaryDirectory(prefix=f"nmap-{ctx.user.id}-{ctx.message.created_at.timestamp():.0f}") as tmp: + with tempfile.TemporaryDirectory(prefix=f"nmap-{ctx.user.id}-{discord.utils.utcnow().timestamp():.0f}") as tmp: tmp_dir = Path(tmp) args = [ "nmap", From 26480d73babbbf50d746e41d7d26c0be01814cf6 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 18:56:18 +0100 Subject: [PATCH 07/13] Just reduce the number of layers --- Dockerfile | 126 ++++++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 68 deletions(-) diff --git a/Dockerfile b/Dockerfile index 22c0378..808dc62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,78 +1,68 @@ -FROM python:3.12 +FROM python:3.12-slim -LABEL org.opencontainers.image.source https://git.i-am.nexus/nex/college-bot-v2 -LABEL org.opencontainers.image.url https://git.i-am.nexus/nex/college-bot-v2 -LABEL org.opencontainers.image.licenses AGPL-3.0 -LABEL org.opencontainers.image.title "College Bot v2" -LABEL org.opencontainers.image.description "Version 2 of jimmy." +LABEL org.opencontainers.image.source="https://git.i-am.nexus/nex/college-bot-v2" +LABEL org.opencontainers.image.url="https://git.i-am.nexus/nex/college-bot-v2" +LABEL org.opencontainers.image.licenses="AGPL-3.0" +LABEL org.opencontainers.image.title="College Bot v2" +LABEL org.opencontainers.image.description="Version 2 of jimmy." WORKDIR /app -RUN DEBIAN_FRONTEND=noninteractive apt-get update - # Install chrome dependencies -#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ -#webext-ublock-origin-chromium \ -#gconf-service \ -#libasound2 \ -#libatk1.0-0 \ -#libc6 \ -#libcairo2 \ -#libcups2 \ -#libdbus-1-3 \ -#libexpat1 \ -#libfontconfig1 \ -#libgcc1 \ -#libgconf-2-4 \ -#libgdk-pixbuf2.0-0 \ -#libglib2.0-0 \ -#libgtk-3-0 \ -#libnspr4 \ -#libpango-1.0-0 \ -#libpangocairo-1.0-0 \ -#libstdc++6 \ -#libx11-6 \ -#libx11-xcb1 \ -#libxcb1 \ -#libxcomposite1 \ -#libxcursor1 \ -#libxdamage1 \ -#libxext6 \ -#libxfixes3 \ -#libxi6 \ -#libxrandr2 \ -#libxrender1 \ -#libxss1 \ -#libxtst6 \ -#ca-certificates \ -#fonts-liberation \ -#libappindicator1 \ -#libnss3 \ -#lsb-release \ -#xdg-utils -# -#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ -# chromium-common \ -# chromium-driver \ -# chromium-sandbox - -# Install general utilities -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - traceroute \ - iputils-ping \ - dnsutils \ - net-tools \ - git \ - whois \ - curl \ - libmagic-dev - -RUN python3 -m venv /app/venv -RUN /app/venv/bin/pip install --upgrade --no-input pip wheel setuptools +RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ +webext-ublock-origin-chromium \ +gconf-service \ +libasound2 \ +libatk1.0-0 \ +libc6 \ +libcairo2 \ +libcups2 \ +libdbus-1-3 \ +libexpat1 \ +libfontconfig1 \ +libgcc1 \ +libgconf-2-4 \ +libgdk-pixbuf2.0-0 \ +libglib2.0-0 \ +libgtk-3-0 \ +libnspr4 \ +libpango-1.0-0 \ +libpangocairo-1.0-0 \ +libstdc++6 \ +libx11-6 \ +libx11-xcb1 \ +libxcb1 \ +libxcomposite1 \ +libxcursor1 \ +libxdamage1 \ +libxext6 \ +libxfixes3 \ +libxi6 \ +libxrandr2 \ +libxrender1 \ +libxss1 \ +libxtst6 \ +ca-certificates \ +fonts-liberation \ +libappindicator1 \ +libnss3 \ +lsb-release \ +xdg-utils \ +chromium-common \ +chromium-driver \ +chromium-sandbox \ +traceroute \ +iputils-ping \ +dnsutils \ +net-tools \ +git \ +whois \ +curl \ +libmagic-dev COPY requirements.txt /tmp/requirements.txt -RUN /app/venv/bin/pip install -Ur /tmp/requirements.txt --no-input +RUN pip install -Ur /tmp/requirements.txt --no-input COPY ./src/ /app/ -CMD ["/app/venv/bin/python", "main.py"] +CMD ["python", "main.py"] From 5a5e17211149a1d0649982fe910e405f194bd6df Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 19:10:13 +0100 Subject: [PATCH 08/13] re-include missing dependencies in image --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 808dc62..275b821 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,7 +58,10 @@ net-tools \ git \ whois \ curl \ -libmagic-dev +libmagic-dev \ +nmap \ +ffmpeg \ + COPY requirements.txt /tmp/requirements.txt RUN pip install -Ur /tmp/requirements.txt --no-input From 86a30fddb5cf8ec26d62262f0c98d32262f5a02e Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 19:10:34 +0100 Subject: [PATCH 09/13] fix: re-include missing dependencies in image --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 275b821..897bd5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,8 +60,7 @@ whois \ curl \ libmagic-dev \ nmap \ -ffmpeg \ - +ffmpeg COPY requirements.txt /tmp/requirements.txt RUN pip install -Ur /tmp/requirements.txt --no-input From 1643eb0c2582d22db15622d1827709fbf98db0df Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 20:01:58 +0100 Subject: [PATCH 10/13] Fix technique default --- src/cogs/net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cogs/net.py b/src/cogs/net.py index 91bab30..b8085ee 100644 --- a/src/cogs/net.py +++ b/src/cogs/net.py @@ -385,9 +385,9 @@ class NetworkCog(commands.Cog): discord.OptionChoice(name="TCP FIN", value="F"), discord.OptionChoice(name="TCP XMAS", value="X"), ], - default="sT" + default="T" ) - ], + ] = "T", treat_all_hosts_online: bool = False, service_scan: bool = False, fast_mode: bool = False, From 0ea4d4264eb8e4c4721d9f8c24b73bcbea7516da Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 20:07:42 +0100 Subject: [PATCH 11/13] Add additional flags to nmap --- src/cogs/net.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cogs/net.py b/src/cogs/net.py index b8085ee..150148f 100644 --- a/src/cogs/net.py +++ b/src/cogs/net.py @@ -429,6 +429,8 @@ class NetworkCog(commands.Cog): "-T", str(timing), "-s" + technique, + "--reason", + "--noninteractive" ] if treat_all_hosts_online: args.append("-Pn") From 0ea24df64e40c863399824a4fb3bf0a3c3cd2c56 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 4 Jul 2024 01:53:08 +0100 Subject: [PATCH 12/13] Add election countdown --- src/cogs/election.py | 61 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/cogs/election.py b/src/cogs/election.py index b490787..9a71cc4 100644 --- a/src/cogs/election.py +++ b/src/cogs/election.py @@ -4,12 +4,13 @@ This module is only meant to be loaded during election times. import asyncio import datetime import logging +import random import re import discord import httpx from bs4 import BeautifulSoup -from discord.ext import commands +from discord.ext import commands, tasks SPAN_REGEX = re.compile( @@ -39,10 +40,66 @@ class ElectionCog(commands.Cog): "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 " "Safari/537.36", } + ETA = datetime.datetime( + 2024, + 7, + 4, + 23, + 30, + tzinfo=datetime.datetime.now().astimezone().tzinfo + ) def __init__(self, bot): - self.bot = bot + self.bot: commands.Bot = bot self.log = logging.getLogger("jimmy.cogs.election") + self.countdown_message = None + self.check_election.start() + + def cog_unload(self) -> None: + self.check_election.cancel() + + @tasks.loop(minutes=1) + async def check_election(self): + if not self.bot.is_ready(): + await self.bot.wait_until_ready() + + guild = self.bot.get_guild(994710566612500550) + if not guild: + return self.log.error("Nonsense guild not found. Can't do countdown.") + channel = discord.utils.get(guild.text_channels, name="countdown") + if not channel: + return self.log.error("Countdown channel not found.") + + await asyncio.sleep(random.randint(0, 10)) + now = discord.utils.utcnow() + diff = (self.ETA - now).total_seconds() + if diff < -86400: + return self.log.debug("Countdown long expired.") + + if diff > 3600: + hours, remainder = map(round, divmod(diff, 3600)) + minutes, seconds = map(round, divmod(remainder, 60)) + message = f"+ {hours} hours, {minutes} minutes, and {seconds} seconds." + elif diff > 60: + minutes, seconds = map(round, divmod(diff, 60)) + message = f"+ {minutes} minutes and {seconds} seconds." + elif diff >= 0: + message = f"+ {round(diff)} seconds." + else: + message = "Results time!" + + if self.countdown_message: + try: + return await self.countdown_message.edit( + content=f"```diff\n{message}```" + ) + except discord.HTTPException: + self.log.exception("Failed to edit countdown message.") + self.countdown_message = await channel.send( + f"```diff\n{message}```" + ) + self.log.debug("Sent countdown message") + def process_soup(self, soup: BeautifulSoup) -> dict[str, list[int]] | None: good_soup = soup.find(attrs={"data-testid": "election-banner-results-bar"}) From 5401cafb6f6b7ebd90b7543311c8c8c3e0019944 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Thu, 4 Jul 2024 01:54:14 +0100 Subject: [PATCH 13/13] Fix missing import --- src/cogs/election.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cogs/election.py b/src/cogs/election.py index 7f55190..cd59bf7 100644 --- a/src/cogs/election.py +++ b/src/cogs/election.py @@ -4,6 +4,7 @@ This module is only meant to be loaded during election times. import asyncio import logging import random +import datetime import re import discord