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.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 \
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \
|
||||
webext-ublock-origin-chromium \
|
||||
gconf-service \
|
||||
libasound2 \
|
||||
|
@ -49,35 +47,24 @@ 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
|
||||
|
||||
# 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
|
||||
xdg-utils \
|
||||
chromium-common \
|
||||
chromium-driver \
|
||||
chromium-sandbox \
|
||||
traceroute \
|
||||
iputils-ping \
|
||||
dnsutils \
|
||||
net-tools \
|
||||
git \
|
||||
whois \
|
||||
curl \
|
||||
libmagic-dev \
|
||||
nmap \
|
||||
ffmpeg
|
||||
|
||||
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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
This module is only meant to be loaded during election times.
|
||||
"""
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
import random
|
||||
import datetime
|
||||
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_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 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:
|
||||
|
|
|
@ -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
|
||||
|
@ -22,22 +23,33 @@ class MeterCog(commands.Cog):
|
|||
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))
|
||||
)
|
||||
)
|
||||
|
||||
|
|
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 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 +24,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):
|
||||
|
@ -43,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:
|
||||
|
@ -69,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"]
|
||||
|
@ -215,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 = {
|
||||
|
@ -346,6 +364,132 @@ 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="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):
|
||||
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")
|
||||
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
|
||||
|
@ -752,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=">>> ",
|
||||
|
|
|
@ -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":
|
||||
|
|
Loading…
Reference in a new issue