Merge branch 'master' of i-am.nexus:nex/college-bot-v2
All checks were successful
Build and Publish / build_and_publish (push) Successful in 1m25s
All checks were successful
Build and Publish / build_and_publish (push) Successful in 1m25s
This commit is contained in:
commit
da8423bb97
11 changed files with 352 additions and 50 deletions
59
Dockerfile
59
Dockerfile
|
@ -1,17 +1,15 @@
|
||||||
FROM python:3.11-slim
|
FROM python:3.12-slim
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source https://git.i-am.nexus/nex/college-bot-v2
|
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.url="https://git.i-am.nexus/nex/college-bot-v2"
|
||||||
LABEL org.opencontainers.image.licenses AGPL-3.0
|
LABEL org.opencontainers.image.licenses="AGPL-3.0"
|
||||||
LABEL org.opencontainers.image.title "College Bot v2"
|
LABEL org.opencontainers.image.title="College Bot v2"
|
||||||
LABEL org.opencontainers.image.description "Version 2 of jimmy."
|
LABEL org.opencontainers.image.description="Version 2 of jimmy."
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update
|
|
||||||
|
|
||||||
# Install chrome dependencies
|
# Install chrome dependencies
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \
|
||||||
webext-ublock-origin-chromium \
|
webext-ublock-origin-chromium \
|
||||||
gconf-service \
|
gconf-service \
|
||||||
libasound2 \
|
libasound2 \
|
||||||
|
@ -49,35 +47,24 @@ fonts-liberation \
|
||||||
libappindicator1 \
|
libappindicator1 \
|
||||||
libnss3 \
|
libnss3 \
|
||||||
lsb-release \
|
lsb-release \
|
||||||
xdg-utils
|
xdg-utils \
|
||||||
|
chromium-common \
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
chromium-driver \
|
||||||
chromium-common \
|
chromium-sandbox \
|
||||||
chromium-driver \
|
traceroute \
|
||||||
chromium-sandbox
|
iputils-ping \
|
||||||
|
dnsutils \
|
||||||
# Install general utilities
|
net-tools \
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
git \
|
||||||
traceroute \
|
whois \
|
||||||
iputils-ping \
|
curl \
|
||||||
dnsutils \
|
libmagic-dev \
|
||||||
net-tools \
|
nmap \
|
||||||
git \
|
ffmpeg
|
||||||
whois \
|
|
||||||
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
|
|
||||||
|
|
||||||
COPY requirements.txt /tmp/requirements.txt
|
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/
|
COPY ./src/ /app/
|
||||||
|
|
||||||
CMD ["/app/venv/bin/python", "main.py"]
|
CMD ["python", "main.py"]
|
||||||
|
|
|
@ -34,6 +34,7 @@ allowed_models = [
|
||||||
# Note that every model has a tag called "latest" which is the most recent version.
|
# 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
|
base_url = "http://ollama:11434/api" # this is the default if you're running via docker compose
|
||||||
|
is_gpu = false
|
||||||
|
|
||||||
[ollama.external]
|
[ollama.external]
|
||||||
owner = 421698654189912064
|
owner = 421698654189912064
|
||||||
|
|
|
@ -9,6 +9,11 @@ services:
|
||||||
- ./jimmy.log:/app/jimmy.log
|
- ./jimmy.log:/app/jimmy.log
|
||||||
- /dev/dri:/dev/dri
|
- /dev/dri:/dev/dri
|
||||||
- jimmy-data:/app/data
|
- 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:
|
extra_hosts:
|
||||||
- host.docker.internal:host-gateway
|
- host.docker.internal:host-gateway
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
|
@ -20,3 +20,4 @@ python-magic~=0.4
|
||||||
aiofiles~=23.2
|
aiofiles~=23.2
|
||||||
fuzzywuzzy[speedup]~=0.18
|
fuzzywuzzy[speedup]~=0.18
|
||||||
tortoise-orm[asyncpg]~=0.21
|
tortoise-orm[asyncpg]~=0.21
|
||||||
|
superpaste @ git+https://github.com/nexy7574/superpaste.git@e31eca6
|
||||||
|
|
|
@ -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 asyncio
|
||||||
import io
|
import io
|
||||||
|
import shutil
|
||||||
import typing
|
import typing
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
import logging
|
import logging
|
||||||
|
@ -105,6 +111,12 @@ class AutoResponder(commands.Cog):
|
||||||
:param uri: The URI to transcode
|
:param uri: The URI to transcode
|
||||||
:return: A transcoded file
|
: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
|
last_reaction: str | None = None
|
||||||
|
|
||||||
def update_reaction(new: str | None = None) -> None:
|
def update_reaction(new: str | None = None) -> None:
|
||||||
|
@ -118,9 +130,7 @@ class AutoResponder(commands.Cog):
|
||||||
last_reaction = new
|
last_reaction = new
|
||||||
self.log.info("Waiting for transcode lock to release")
|
self.log.info("Waiting for transcode lock to release")
|
||||||
async with self.transcode_lock:
|
async with self.transcode_lock:
|
||||||
cog: FFMeta = self.bot.get_cog("FFMeta")
|
cog = FFMeta(self.bot)
|
||||||
if not cog:
|
|
||||||
raise RuntimeError("FFMeta cog not loaded")
|
|
||||||
if not isinstance(uri, str):
|
if not isinstance(uri, str):
|
||||||
uri = str(uri)
|
uri = str(uri)
|
||||||
|
|
||||||
|
@ -240,6 +250,12 @@ class AutoResponder(commands.Cog):
|
||||||
*domains: str,
|
*domains: str,
|
||||||
additional: Iterable[str] = None
|
additional: Iterable[str] = None
|
||||||
) -> 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:
|
if self.allow_hevc_to_h264 is False:
|
||||||
self.log.debug("hevc_to_h264 is disabled, not engaging.")
|
self.log.debug("hevc_to_h264 is disabled, not engaging.")
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
This module is only meant to be loaded during election times.
|
This module is only meant to be loaded during election times.
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
|
import random
|
||||||
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import httpx
|
import httpx
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from discord.ext import commands
|
from discord.ext import commands, tasks
|
||||||
|
|
||||||
|
|
||||||
SPAN_REGEX = re.compile(
|
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 "
|
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 "
|
||||||
"Safari/537.36",
|
"Safari/537.36",
|
||||||
}
|
}
|
||||||
|
ETA = datetime.datetime(
|
||||||
|
2024,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
23,
|
||||||
|
30,
|
||||||
|
tzinfo=datetime.datetime.now().astimezone().tzinfo
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot: commands.Bot = bot
|
||||||
self.log = logging.getLogger("jimmy.cogs.election")
|
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:
|
def process_soup(self, soup: BeautifulSoup) -> dict[str, list[int]] | None:
|
||||||
good_soups = list(soup.find_all(attrs={"data-testid": "election-banner-results-bar"}))
|
good_soups = list(soup.find_all(attrs={"data-testid": "election-banner-results-bar"}))
|
||||||
|
|
|
@ -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 asyncio
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import typing
|
import typing
|
||||||
|
@ -56,6 +62,8 @@ class FFMeta(commands.Cog):
|
||||||
@commands.slash_command()
|
@commands.slash_command()
|
||||||
async def ffprobe(self, ctx: discord.ApplicationContext, url: str = None, attachment: discord.Attachment = None):
|
async def ffprobe(self, ctx: discord.ApplicationContext, url: str = None, attachment: discord.Attachment = None):
|
||||||
"""Runs ffprobe on a given URL or attachment"""
|
"""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 url is None:
|
||||||
if attachment is None:
|
if attachment is None:
|
||||||
return await ctx.respond("No URL or attachment provided")
|
return await ctx.respond("No URL or attachment provided")
|
||||||
|
@ -142,6 +150,10 @@ class FFMeta(commands.Cog):
|
||||||
] = False,
|
] = False,
|
||||||
):
|
):
|
||||||
"""Converts a given URL or attachment to an Opus file"""
|
"""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:
|
if bitrate == 0:
|
||||||
bitrate = 0.5
|
bitrate = 0.5
|
||||||
if mono:
|
if mono:
|
||||||
|
@ -244,6 +256,8 @@ class FFMeta(commands.Cog):
|
||||||
rbh = Path("assets/right-behind-you.ogg").resolve()
|
rbh = Path("assets/right-behind-you.ogg").resolve()
|
||||||
if not rbh.exists():
|
if not rbh.exists():
|
||||||
return await ctx.respond("The file `right-behind-you.ogg` is missing.")
|
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"):
|
if not image.content_type.startswith("image"):
|
||||||
return await ctx.respond("That's not an image!")
|
return await ctx.respond("That's not an image!")
|
||||||
with tempfile.NamedTemporaryFile(suffix=Path(image.filename).suffix) as temp:
|
with tempfile.NamedTemporaryFile(suffix=Path(image.filename).suffix) as temp:
|
||||||
|
|
|
@ -13,6 +13,7 @@ class MeterCog(commands.Cog):
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
|
|
||||||
@commands.slash_command(name="gay-meter")
|
@commands.slash_command(name="gay-meter")
|
||||||
|
@discord.guild_only()
|
||||||
async def gay_meter(self, ctx: discord.ApplicationContext, user: discord.User = None):
|
async def gay_meter(self, ctx: discord.ApplicationContext, user: discord.User = None):
|
||||||
"""Checks how gay someone is"""
|
"""Checks how gay someone is"""
|
||||||
user = user or ctx.user
|
user = user or ctx.user
|
||||||
|
@ -22,6 +23,9 @@ class MeterCog(commands.Cog):
|
||||||
await ctx.edit(content="Calculating... %d%%" % i)
|
await ctx.edit(content="Calculating... %d%%" % i)
|
||||||
await asyncio.sleep(random.randint(1, 30) / 10)
|
await asyncio.sleep(random.randint(1, 30) / 10)
|
||||||
|
|
||||||
|
if random.randint(0, 1):
|
||||||
|
pct = sum((user.id, ctx.user.id, ctx.channel.id, ctx.guild.id)) % 400
|
||||||
|
else:
|
||||||
pct = user.id % 100
|
pct = user.id % 100
|
||||||
await ctx.edit(content=f"{user.mention} is {pct}% gay.")
|
await ctx.edit(content=f"{user.mention} is {pct}% gay.")
|
||||||
|
|
||||||
|
@ -29,15 +33,23 @@ class MeterCog(commands.Cog):
|
||||||
async def penis_meter(self, ctx: discord.ApplicationContext, user: discord.User = None):
|
async def penis_meter(self, ctx: discord.ApplicationContext, user: discord.User = None):
|
||||||
"""Checks the length of someone's penis."""
|
"""Checks the length of someone's penis."""
|
||||||
user = user or ctx.user
|
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 = ["8"]
|
||||||
chunks += ["="] * n
|
chunks += ["="] * pct
|
||||||
chunks.append("B")
|
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(
|
return await ctx.respond(
|
||||||
embed=discord.Embed(
|
embed=discord.Embed(
|
||||||
title=f"{user.display_name}'s penis length:",
|
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))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
144
src/cogs/net.py
144
src/cogs/net.py
|
@ -1,10 +1,20 @@
|
||||||
|
# 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 asyncio
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import typing
|
import typing
|
||||||
|
import warnings
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
@ -14,6 +24,8 @@ from dns import asyncresolver
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.tree import Tree
|
from rich.tree import Tree
|
||||||
from conf import CONFIG
|
from conf import CONFIG
|
||||||
|
from superpaste import HstSHBackend
|
||||||
|
from superpaste.backends import GenericFile
|
||||||
|
|
||||||
|
|
||||||
class GetFilteredTextView(discord.ui.View):
|
class GetFilteredTextView(discord.ui.View):
|
||||||
|
@ -43,6 +55,8 @@ class NetworkCog(commands.Cog):
|
||||||
@commands.slash_command()
|
@commands.slash_command()
|
||||||
async def ping(self, ctx: discord.ApplicationContext, target: str = None):
|
async def ping(self, ctx: discord.ApplicationContext, target: str = None):
|
||||||
"""Get the bot's latency, or the network latency to a target."""
|
"""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:
|
if target is None:
|
||||||
return await ctx.respond(f"Pong! {round(self.bot.latency * 1000)}ms")
|
return await ctx.respond(f"Pong! {round(self.bot.latency * 1000)}ms")
|
||||||
else:
|
else:
|
||||||
|
@ -69,6 +83,8 @@ class NetworkCog(commands.Cog):
|
||||||
@commands.slash_command()
|
@commands.slash_command()
|
||||||
async def whois(self, ctx: discord.ApplicationContext, target: str):
|
async def whois(self, ctx: discord.ApplicationContext, target: str):
|
||||||
"""Get information about a user."""
|
"""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):
|
async def run_command(with_disclaimer: bool = False):
|
||||||
args = [] if with_disclaimer else ["-H"]
|
args = [] if with_disclaimer else ["-H"]
|
||||||
|
@ -215,6 +231,8 @@ class NetworkCog(commands.Cog):
|
||||||
await ctx.defer()
|
await ctx.defer()
|
||||||
if re.search(r"\s+", url):
|
if re.search(r"\s+", url):
|
||||||
return await ctx.respond("URL cannot contain spaces.")
|
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"]
|
args = ["sudo", "-E", "-n", "traceroute"]
|
||||||
flags = {
|
flags = {
|
||||||
|
@ -346,6 +364,132 @@ class NetworkCog(commands.Cog):
|
||||||
|
|
||||||
await ctx.respond(embed=embed)
|
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="T"
|
||||||
|
)
|
||||||
|
] = "T",
|
||||||
|
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}-{discord.utils.utcnow().timestamp():.0f}") as tmp:
|
||||||
|
tmp_dir = Path(tmp)
|
||||||
|
args = [
|
||||||
|
"nmap",
|
||||||
|
"-oA",
|
||||||
|
str(tmp_dir.resolve() / target),
|
||||||
|
"-T",
|
||||||
|
str(timing),
|
||||||
|
"-s" + technique,
|
||||||
|
"--reason",
|
||||||
|
"--noninteractive"
|
||||||
|
]
|
||||||
|
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):
|
def setup(bot):
|
||||||
bot.add_cog(NetworkCog(bot))
|
bot.add_cog(NetworkCog(bot))
|
||||||
|
|
|
@ -461,6 +461,28 @@ class PromptSelector(discord.ui.View):
|
||||||
|
|
||||||
@discord.ui.button(label="Done", style=discord.ButtonStyle.success, custom_id="done")
|
@discord.ui.button(label="Done", style=discord.ButtonStyle.success, custom_id="done")
|
||||||
async def done(self, btn: discord.ui.Button, interaction: Interaction):
|
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()
|
self.stop()
|
||||||
|
|
||||||
|
|
||||||
|
@ -637,6 +659,24 @@ class Ollama(commands.Cog):
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
try:
|
try:
|
||||||
server = self.next_server(tried)
|
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:
|
except RuntimeError:
|
||||||
tried.add(server)
|
tried.add(server)
|
||||||
continue
|
continue
|
||||||
|
@ -752,6 +792,27 @@ class Ollama(commands.Cog):
|
||||||
|
|
||||||
key = os.urandom(6).hex()
|
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(
|
embed = discord.Embed(
|
||||||
title="Generating response...",
|
title="Generating response...",
|
||||||
description=">>> ",
|
description=">>> ",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
@ -190,6 +191,8 @@ class YTDLCog(commands.Cog):
|
||||||
:param file: The file to convert
|
:param file: The file to convert
|
||||||
:return: The converted file
|
:return: The converted file
|
||||||
"""
|
"""
|
||||||
|
if not shutil.which("ffmpeg"):
|
||||||
|
raise RuntimeError("ffmpeg is not installed.")
|
||||||
new_file = file.with_suffix(".m4a")
|
new_file = file.with_suffix(".m4a")
|
||||||
args = [
|
args = [
|
||||||
"-vn",
|
"-vn",
|
||||||
|
@ -319,8 +322,9 @@ class YTDLCog(commands.Cog):
|
||||||
else:
|
else:
|
||||||
chosen_format = user_format
|
chosen_format = user_format
|
||||||
|
|
||||||
|
ffmpeg_installed = bool(shutil.which("ffmpeg"))
|
||||||
options.setdefault("postprocessors", [])
|
options.setdefault("postprocessors", [])
|
||||||
if audio_only:
|
if audio_only and ffmpeg_installed:
|
||||||
# Overwrite format here to be best audio under 25 megabytes.
|
# Overwrite format here to be best audio under 25 megabytes.
|
||||||
chosen_format = "ba[filesize<20M]"
|
chosen_format = "ba[filesize<20M]"
|
||||||
# Also force sorting by the best audio bitrate first.
|
# Also force sorting by the best audio bitrate first.
|
||||||
|
@ -332,7 +336,7 @@ class YTDLCog(commands.Cog):
|
||||||
options["format"] = chosen_format
|
options["format"] = chosen_format
|
||||||
options["paths"] = paths
|
options["paths"] = paths
|
||||||
|
|
||||||
if subtitles:
|
if subtitles and ffmpeg_installed:
|
||||||
subtitles, burn = subtitles.split("+", 1) if "+" in subtitles else (subtitles, "0")
|
subtitles, burn = subtitles.split("+", 1) if "+" in subtitles else (subtitles, "0")
|
||||||
burn = burn[0].lower() in ("y", "1", "t")
|
burn = burn[0].lower() in ("y", "1", "t")
|
||||||
if subtitles.lower() == "auto":
|
if subtitles.lower() == "auto":
|
||||||
|
|
Loading…
Reference in a new issue