templates/discord-bot: init

This commit is contained in:
Infinidoge 2024-11-20 15:37:16 -05:00
parent 7de0f37a07
commit b29f007e69
Signed by: Infinidoge
SSH key fingerprint: SHA256:oAMyvotlNFraMmZmr+p6AxnNfW/GioTs1pOn3V4tQ7A
8 changed files with 313 additions and 0 deletions

View file

@ -0,0 +1,2 @@
watch_file "flake.nix" "pyproject.toml"
use flake

14
templates/discord-bot/.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
# Secrets
.env
# Nix
.direnv/
result
# Python
.ruff_cache
.ropeproject
# Runtime
rename.sqlite3
rename.sqlite3-journal

View file

@ -0,0 +1,66 @@
{
description = "Template for Python-based Discord bots";
inputs = {
nixpkgs.url = "github:Infinidoge/nixpkgs/feat/disnake";
flake-parts.url = "github:hercules-ci/flake-parts";
devshell.url = "github:numtide/devshell";
pyproject-nix.url = "github:nix-community/pyproject.nix";
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
devshell.inputs.nixpkgs.follows = "nixpkgs";
pyproject-nix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" ];
imports = with inputs; [
devshell.flakeModule
];
perSystem = { config, pkgs, ... }:
let
project = inputs.pyproject-nix.lib.project.loadPyproject {
projectRoot = ./.;
};
python = pkgs.python3;
in
{
packages.default = (python.pkgs.buildPythonPackage (
project.renderers.buildPythonPackage { inherit python; }
)).overrideAttrs { allowSubstitutes = false; preferLocalBuild = true; };
devshells.default =
let
env = python.withPackages (
project.renderers.withPackages {
inherit python;
extraPackages = p: with p; [
python-lsp-server
python-lsp-ruff
pylsp-rope
isort
black
];
}
);
in
{
devshell = {
name = "rename";
motd = "";
packages = [
env
];
};
env = [
{ name = "PYTHONPATH"; prefix = "${env}/${env.sitePackages}"; }
];
};
};
};
}

View file

@ -0,0 +1,21 @@
[project]
name = "rename"
version = "0.0.1"
dependencies = [
"disnake",
"python-dotenv",
"aiosqlite"
]
[project.scripts]
rename = "rename:run"
[tool.setuptools.packages]
find = {}
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[tool.ruff]
line-length = 120

View file

@ -0,0 +1 @@
from .main import run

View file

@ -0,0 +1,25 @@
import aiosqlite
async def setup_db(db_file):
db = await aiosqlite.connect(db_file)
await db.executescript("""
BEGIN;
CREATE TABLE IF NOT EXISTS guilds (
guild_id INTEGER NOT NULL PRIMARY KEY,
prefix TEXT NOT NULL DEFAULT ">"
)
WITHOUT ROWID;
# DB Setup here
COMMIT;
PRAGMA optimize(0x10002);
PRAGMA main.synchronous = NORMAL;
""")
return db

View file

@ -0,0 +1,154 @@
import logging
import os
import sys
from disnake import Guild, Intents, Message
from disnake.ext import commands
from disnake.ext.commands import Bot
from dotenv import find_dotenv, load_dotenv
from .db import setup_db
from .notifications import Notifications
from .settings import Settings
# Logger setup
logger_disnake = logging.getLogger("disnake")
logger_disnake.setLevel(logging.WARNING)
log = logging.getLogger("rename")
log.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s")
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
logger_disnake.addHandler(handler)
log.addHandler(handler)
if load_dotenv(find_dotenv(usecwd=True)):
log.debug("Loaded .env")
else:
log.debug("Didn't find .env")
TOKEN = os.getenv("TOKEN")
DB_FILE = os.getenv("DB_FILE") or "rename.sqlite3"
DEFAULT_PREFIX = os.getenv("DEFAULT_PREFIX") or ">>>"
async def get_prefix(the_bot, message: Message):
if not message.guild:
return commands.when_mentioned_or(DEFAULT_PREFIX)(the_bot, message)
gp = await the_bot.get_guild_prefix(message.guild)
if gp == "":
return commands.when_mentioned(the_bot, message)
return commands.when_mentioned_or(gp)(the_bot, message)
class Rename(Bot):
def __init__(self, prefix, description=None, **options):
super().__init__(
prefix,
description=description,
command_sync_flags=options.get("sync_flags"),
allowed_mentions=options.get("allowed_mentions"),
intents=options.get("intents"),
)
self.db = self.loop.run_until_complete(setup_db(DB_FILE))
self.prefixes = {}
async def get_guild_prefix(self, guild: Guild):
if guild.id in self.prefixes:
return self.prefixes[guild.id]
cur = await self.db.execute(
"SELECT prefix FROM guilds WHERE guild_id=? LIMIT 1",
[guild.id],
)
prefix = await cur.fetchone() or [DEFAULT_PREFIX]
prefix = prefix[0]
self.prefixes[guild.id] = prefix
return prefix
async def set_guild_prefix(self, guild: Guild, prefix):
await self.db.execute(
"REPLACE INTO guilds VALUES(?, ?)",
(guild.id, prefix),
)
self.prefixes[guild.id] = prefix
async def close(self):
await super().close()
await self.db.close()
bot = Rename(
prefix=get_prefix,
description="@description@",
intents=Intents(
guilds=True,
messages=True,
message_content=True,
),
sync_flags=commands.CommandSyncFlags(
sync_commands=False,
),
)
@bot.event
async def on_ready():
log.info("Logged in as:")
log.info(bot.user)
log.info(bot.user.id)
log.info("-------------")
@bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.CommandNotFound):
return
if isinstance(error, commands.NoPrivateMessage):
await ctx.send(error)
return
log.exception(error)
await ctx.send(error)
@bot.command()
@commands.is_owner()
async def echo(ctx, arg):
await ctx.send(arg)
@bot.command()
async def ping(ctx):
await ctx.send("Pong")
@bot.command()
@commands.guild_only()
@commands.is_owner()
async def prefix(ctx, prefix=None):
if prefix is None:
prefix = await ctx.bot.get_guild_prefix(ctx.guild)
if prefix == "":
await ctx.send(f"{ctx.guild.name} prefix is mentioning me only")
else:
await ctx.send(f"{ctx.guild.name} prefix is `{prefix}`")
else:
await ctx.bot.set_guild_prefix(ctx.guild, prefix)
if prefix == "":
await ctx.send(f"Set {ctx.guild.name} prefix to mentioning me only")
else:
await ctx.send(f"Set {ctx.guild.name} prefix to `{prefix}`")
def run():
bot.run(TOKEN)

30
templates/discord-bot/setup Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# vim: set ft=bash:
set -e
if [ $# -lt 3 ]; then
echo "Not enough arguments"
echo 'usage: ./setup basename ClassName "description here"'
exit 1
fi
nameBase=$1
nameClass=$2
description=$3
files=(
"flake.nix"
".gitignore"
"pyproject.toml"
"rename/main.py"
)
for file in ${files[@]}; do
sed -i "
s/rename/$nameBase/g;
s/Rename/$nameClass/g;
s/@description@/$description/g;
" $file
done
mv rename $nameBase