Add more config options
All checks were successful
Build and Publish Jimmy.2 / build_and_publish (push) Successful in 9s

And also document those config options better
This commit is contained in:
Nexus 2024-04-29 01:29:48 +01:00
parent 020a698ba6
commit 68edb16337
Signed by: nex
GPG key ID: 0FA334385D0B689F
8 changed files with 113 additions and 18 deletions

View file

@ -1,6 +1,10 @@
# Example config file for Jimmy v2.
# This file is populated mostly with the default values set in src/conf.py.
[jimmy]
token = "token" # the bot token
debug_guilds = [994710566612500550] # server IDs to create slash commands in. Set to null for all guilds.
debug_guilds = [994710566612500550] # server IDs to create slash commands in. Omit for all guilds.
disabled_commands = ["i do not exist"] # A list of full command names to disable (e.g. "<group name> <subcommand", "command_name").
modules = ["cogs.*"] # a list of modules to load. Glob-based patterns can be used. Defaults to all cogs if ommitted
[logging]
level = "DEBUG" # can be one of DEBUG, INFO, WARNING, ERROR, CRITICAL. Defaults to INFO
@ -15,6 +19,8 @@ suppress = [
]
# All the loggers specified here will have their log level set to WARNING.
# Ollama server setups. Omit all `ollama.*` to disable all AI-related features.
[ollama.internal]
# name is "internal"
owner = 421698654189912064 # who owns the server
@ -29,5 +35,39 @@ base_url = "http://ollama:11434/api" # this is the default if you're running vi
owner = 421698654189912064
allowed_models = ["*"]
base_url = "http://example.com/api"
icon_url = "http://example.com/favicon.png"
icon_url = "http://example.com/favicon.png" # An icon to use in the footer of embeds
[screenshot]
# Configuration for the /screenshot command.
proxy = "http://example.test:8888" # the HTTP/S/OCKS proxy to use
[quote_a]
# configuration for the /quota command.
# channel = 123456
# ^ The channel ID. If omitted, will default to a channel called "quotes".
[quote_a.names]
foo = "bar"
# A key = "Value" mapping of names to true names. For example:
# foo = "bar"
# foobar = "bar"
# ipv6 = "security"
[redis]
# Redis configuration. If you are using the docker-compose setup, then you do not need to configure this.
host = "redis" # the host[name or IP] of the redis server
port = 6379 # self-explanatory
no_ping = false # disables the startup health-check. Not recommended to set to true.
[responder]
# Configures the auto-responder.
overpowered_by = [421698654189912064] # if these users are present in the server's member cache, auto-responses will be disabled in that server.
overrule_offline_superiors = true # if all the overpower users are missing or offline, ignore overpower rules
downloads_pdfs = true # If False, PDF links will not be downloaded and uploaded to discord.
[responder.transcoding]
# Configured specifically automated transcoding.
enabled = true # whether any transcoding at all should be done. If false, no links or attachments are ever probed.
hevc = true # Enables probing video links and converting any HEVC videos to H264+opus.
on_demand = true # Enables transcoding any video to h264+opus when 📼 (VHS) is reacted.

View file

@ -14,6 +14,7 @@ import httpx
from discord.ext import commands
from .ffmeta import FFMeta
from conf import CONFIG
class AutoResponder(commands.Cog):
@ -21,6 +22,42 @@ class AutoResponder(commands.Cog):
self.bot = bot
self.log = logging.getLogger("jimmy.cogs.auto_responder")
self.transcode_lock = asyncio.Lock()
self.config = CONFIG["responder"]
if self.config.get("overpowered_by") and bot.intents.members is False:
raise ValueError(
"overpowered_by is set, however members intent is disabled, making it useless."
)
if self.config.get("overrule_offline_superiors", True) is True and bot.intents.presences is False:
raise ValueError(
"overrule_offline_superiors is enabled, however presences intent is not!"
)
self.config.setdefault("transcoding", {"enabled": True, "hevc": True, "on_demand": True})
@property
def transcoding_enabled(self) -> bool:
return self.config["transcoding"].get("enabled", True)
@property
def allow_hevc_to_h264(self) -> bool:
return self.transcoding_enabled and self.config["transcoding"].get("hevc", True)
@property
def allow_on_demand_transcoding(self) -> bool:
return self.transcoding_enabled and self.config["transcoding"].get("on_demand", True)
def overpowered(self, guild: discord.Guild | None) -> bool:
if not guild:
self.log.debug("Not overpowered, in DM")
return False # in a DM
enable_offline = self.config.get("overrule_offline_superiors", False)
for _id in self.config.get("overpowered_by", []):
member = guild.get_member(_id)
if member:
if member.status != discord.Status.offline or enable_offline is False:
self.log.debug("Overpowered by %s (%s)", member, member.status)
return True
self.log.debug("No overpowered.")
return False
@staticmethod
@typing.overload
@ -71,7 +108,6 @@ class AutoResponder(commands.Cog):
if new:
_ = asyncio.create_task(update.add_reaction(new))
last_reaction = new
self.log.info("Waiting for transcode lock to release")
async with self.transcode_lock:
cog: FFMeta = self.bot.get_cog("FFMeta")
@ -195,6 +231,9 @@ class AutoResponder(commands.Cog):
*domains: str,
additional: Iterable[str] = None
) -> None:
if self.allow_hevc_to_h264 is False:
self.log.debug("hevc_to_h264 is disabled, not engaging.")
return
additional = additional or []
links = self.extract_links(message.content, *domains) + list(additional)
if links:
@ -259,6 +298,9 @@ class AutoResponder(commands.Cog):
async def copy_ncfe_docs(self, message: discord.Message, links: list[ParseResult]) -> None:
files = []
if self.config.get("download_pdfs", True) is False:
self.log.debug("Download PDFs is disabled in config, disengaging.")
return
self.log.info("Preparing to download: %s", ", ".join(map(ParseResult.geturl, links)))
async with aiohttp.ClientSession() as session:
for link in set(links):
@ -291,10 +333,10 @@ class AutoResponder(commands.Cog):
@commands.Cog.listener("on_message")
async def auto_responder(self, message: discord.Message):
if message.author == self.bot.user:
if message.author == self.bot.user or self.overpowered(message.guild):
return
# Check for HEVC truth social links and convert into h264
# Check for HEVC links and convert into h264
if message.channel.name == "spam" and message.author != self.bot.user:
await self.transcode_hevc_to_h264(message)
@ -304,7 +346,7 @@ class AutoResponder(commands.Cog):
@commands.Cog.listener("on_reaction_add")
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User):
if user == self.bot.user:
if user == self.bot.user or self.overpowered(reaction.message.guild):
return
if reaction.message.author != self.bot.user:
@ -313,6 +355,7 @@ class AutoResponder(commands.Cog):
extra = []
if reaction.message.attachments:
extra = [attachment.url for attachment in reaction.message.attachments]
if self.allow_on_demand_transcoding:
await self.transcode_hevc_to_h264(reaction.message, additional=extra)
@commands.Cog.listener("on_raw_reaction_add")

View file

@ -1,6 +1,5 @@
import asyncio
import base64
import contextlib
import io
import json
import logging
@ -12,7 +11,6 @@ from fnmatch import fnmatch
import aiohttp
import discord
import httpx
import redis
from discord import Interaction
from discord.ext import commands

View file

@ -3,7 +3,7 @@ import io
import logging
import re
from datetime import datetime, timedelta
from typing import Annotated, Callable, Iterable
from typing import Annotated, Callable
import discord
import matplotlib.pyplot as plt

View file

@ -215,7 +215,7 @@ class ScreenshotCog(commands.Cog):
f"Cleanup time: {seconds(start_cleanup, end_cleanup)}s\n"
f"Total time: {seconds(start_init, end_cleanup)}s\n"
f"Screenshot size: {screenshot_size_mb}MB\n"
f"Resolution: {resolution}"
f"Resolution: {resolution}\n"
f"Used proxy: {use_proxy}",
colour=discord.Colour.dark_theme(),
timestamp=discord.utils.utcnow(),

View file

@ -23,9 +23,9 @@ class YTDLCog(commands.Cog):
self.log = logging.getLogger("jimmy.cogs.ytdl")
self.common_formats = {
"144p": "17", # mp4 (h264+aac) v
"240p": "133+139",
"240p": "bv[width==240]+ba[ext=webm]/bv[width==240]+ba[ext=m4a]/bv[width==240]+ba",
"360p": "18",
"480p": "135+139",
"480p": "bv[width==480]+ba[ext=webm]/bv[width==480]+ba[ext=m4a]/bv[width==480]+ba",
"720p": "22",
"1080p": "bv[width==1080]+ba[ext=webm]/bv[width==1080]+ba[ext=m4a]/bv[width==1080]+ba",
"1440p": "bv[width==1440]+ba[ext=webm]/bv[width==1440]+ba[ext=m4a]/bv[width==1440]+ba",
@ -102,9 +102,13 @@ class YTDLCog(commands.Cog):
:return: The created hash key
"""
snip = snip or "*"
await self._init_db()
async with aiosqlite.connect("./data/ytdl.db") as db:
_hash = hashlib.md5(f"{webpage_url}:{format_id}:{snip}".encode()).hexdigest()
try:
await self._init_db()
except Exception as e:
logging.error("Failed to initialise ytdl database: %s", e, exc_info=True)
return
async with aiosqlite.connect("./data/ytdl.db") as db:
self.log.debug(
"Saving %r (%r:%r:%r) with message %d>%d, index %d",
_hash,
@ -137,7 +141,11 @@ class YTDLCog(commands.Cog):
:param snip: The start and end time to snip the video. e.g. 00:00:00-00:10:00
:return: the URL, if found and valid.
"""
try:
await self._init_db()
except Exception as e:
logging.error("Failed to initialise ytdl database: %s", e, exc_info=True)
return
async with aiosqlite.connect("./data/ytdl.db") as db:
_hash = hashlib.md5(f"{webpage_url}:{format_id}:{snip}".encode()).hexdigest()
self.log.debug(

View file

@ -28,10 +28,8 @@ try:
CONFIG.setdefault("logging", {})
CONFIG.setdefault("jimmy", {})
CONFIG.setdefault("ollama", {})
CONFIG.setdefault("rss", {"meta": {"channel": None}})
CONFIG.setdefault("screenshot", {})
CONFIG.setdefault("quote_a", {"channel": None})
CONFIG.setdefault("server", {"host": "0.0.0.0", "port": 8080, "channel": 1032974266527907901})
CONFIG.setdefault("redis", {"host": "redis", "port": 6379, "decode_responses": True})
except FileNotFoundError:
cwd = Path.cwd()

View file

@ -179,6 +179,14 @@ async def delete_message(ctx: discord.ApplicationContext, message: discord.Messa
await ctx.delete(delay=15)
@bot.check_once
async def check_is_enabled(ctx: commands.Context | discord.ApplicationContext) -> bool:
disabled = CONFIG["jimmy"].get("disabled_commands", [])
if ctx.command.qualified_name in disabled:
raise commands.DisabledCommand("%s is disabled via this instance's configuration file.")
return True
if not CONFIG["jimmy"].get("token"):
log.critical("No token specified in config.toml. Exiting. (hint: set jimmy.token in config.toml)")
sys.exit(1)