init the bot

This commit is contained in:
Schrottkatze 2025-09-01 03:21:58 +02:00
commit 0437c75717
Signed by: schrottkatze
SSH key fingerprint: SHA256:/raZeWZ2RLThYkX/nq26frnmA4Bi3qRM/hijRmDBa10
12 changed files with 3192 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake --log-format multiline-with-logs

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
.direnv
token

2436
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "redaction-bot"
version = "0.1.0"
edition = "2024"
[dependencies]
aho-corasick = "1.1.3"
rand = "0.9.2"
serenity = "0.12.4"
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
unicase = "2.8.1"

74
bad_words.txt Normal file
View file

@ -0,0 +1,74 @@
fag
faggot
gay
ass
cunt
shit
fuck
fucking
fucked
shitting
gayass
anal
blowjob
penis
dick
richard
cock
gock
bastard
hell
toilet
skibidi toilet
skibedy
skibbeddy
skibeddy
skibbedy
skibidi
slut
horny
musk
trump
hitler
nazi
🏳️‍🌈⃠
orwell
1984
fahrenheit
feet
foot
inch
yard
mile
ounce
stupid
kringspiermusketier
god
scheiß
kack
mist
dreck
fick
sex
hure
dumm
missgeburt
bitch
vance
couchfucker
piss
arse
crap
nigger
nigga
twat
idiot
tranny

82
flake.lock generated Normal file
View file

@ -0,0 +1,82 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1755585599,
"narHash": "sha256-tl/0cnsqB/Yt7DbaGMel2RLa7QG5elA8lkaOXli6VdY=",
"owner": "nix-community",
"repo": "fenix",
"rev": "6ed03ef4c8ec36d193c18e06b9ecddde78fb7e42",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1756125398,
"narHash": "sha256-XexyKZpf46cMiO5Vbj+dWSAXOnr285GHsMch8FBoHbc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3b9f00d7a7bf68acd4c4abb9d43695afb04e03a5",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs",
"systems": "systems"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1755504847,
"narHash": "sha256-VX0B9hwhJypCGqncVVLC+SmeMVd/GAYbJZ0MiiUn2Pk=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "a905e3b21b144d77e1b304e49f3264f6f8d4db75",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

41
flake.nix Normal file
View file

@ -0,0 +1,41 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
systems.url = "github:nix-systems/default";
fenix.url = "github:nix-community/fenix";
fenix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
nixpkgs,
fenix,
systems,
...
}:
let
forEachSystem = nixpkgs.lib.genAttrs (import systems);
in
{
devShells = forEachSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
toolchain =
with fenix.packages.${system};
combine [
complete.toolchain
];
in
{
default = pkgs.mkShell rec {
buildInputs = with pkgs; [
cargo-watch
toolchain
];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
};
}
);
};
}

348
meows.txt Normal file
View file

@ -0,0 +1,348 @@
mrrew
mrreww
mrrewww
mrreew
mrreeww
mrreewww
mrrow
mrroww
mrrowww
mrreow
mrreoww
mrreowww
mrraaow
mrraaoww
mrraaowww
mrew
mreww
mrewww
mreew
mreeww
mreewww
mrow
mroww
mrowww
mreow
mreoww
mreowww
mraaow
mraaoww
mraaowww
mew
meww
mewww
meew
meeww
meewww
mow
moww
mowww
meow
meoww
meowww
maaow
maaoww
maaowww
mrriau
mrriauu
mrriauuu
mrriauuuu
mrriaau
mrriaauu
mrriaauuu
mrriaauuuu
mrriaaau
mrriaaauu
mrriaaauuu
mrriaaauuuu
mrriiau
mrriiauu
mrriiauuu
mrriiauuuu
mrriiaau
mrriiaauu
mrriiaauuu
mrriiaauuuu
mrriiaaau
mrriiaaauu
mrriiaaauuu
mrriiaaauuuu
mrrau
mrrauu
mrrauuu
mrrauuuu
mrraau
mrraauu
mrraauuu
mrraauuuu
mrraaau
mrraaauu
mrraaauuu
mrraaauuuu
mriau
mriauu
mriauuu
mriauuuu
mriaau
mriaauu
mriaauuu
mriaauuuu
mriaaau
mriaaauu
mriaaauuu
mriaaauuuu
mriiau
mriiauu
mriiauuu
mriiauuuu
mriiaau
mriiaauu
mriiaauuu
mriiaauuuu
mriiaaau
mriiaaauu
mriiaaauuu
mriiaaauuuu
mrau
mrauu
mrauuu
mrauuuu
mraau
mraauu
mraauuu
mraauuuu
mraaau
mraaauu
mraaauuu
mraaauuuu
miau
miauu
miauuu
miauuuu
miaau
miaauu
miaauuu
miaauuuu
miaaau
miaaauu
miaaauuu
miaaauuuu
miiau
miiauu
miiauuu
miiauuuu
miiaau
miiaauu
miiaauuu
miiaauuuu
miiaaau
miiaaauu
miiaaauuu
miiaaauuuu
mau
mauu
mauuu
mauuuu
maau
maauu
maauuu
maauuuu
maaau
maaauu
maaauuu
maaauuuu
mrrew~
mrreww~
mrrewww~
mrreew~
mrreeww~
mrreewww~
mrrow~
mrroww~
mrrowww~
mrreow~
mrreoww~
mrreowww~
mrraaow~
mrraaoww~
mrraaowww~
mrew~
mreww~
mrewww~
mreew~
mreeww~
mreewww~
mrow~
mroww~
mrowww~
mreow~
mreoww~
mreowww~
mraaow~
mraaoww~
mraaowww~
mew~
meww~
mewww~
meew~
meeww~
meewww~
mow~
moww~
mowww~
meow~
meoww~
meowww~
maaow~
maaoww~
maaowww~
mrriau~
mrriauu~
mrriauuu~
mrriauuuu~
mrriaau~
mrriaauu~
mrriaauuu~
mrriaauuuu~
mrriaaau~
mrriaaauu~
mrriaaauuu~
mrriaaauuuu~
mrriiau~
mrriiauu~
mrriiauuu~
mrriiauuuu~
mrriiaau~
mrriiaauu~
mrriiaauuu~
mrriiaauuuu~
mrriiaaau~
mrriiaaauu~
mrriiaaauuu~
mrriiaaauuuu~
mrrau~
mrrauu~
mrrauuu~
mrrauuuu~
mrraau~
mrraauu~
mrraauuu~
mrraauuuu~
mrraaau~
mrraaauu~
mrraaauuu~
mrraaauuuu~
mriau~
mriauu~
mriauuu~
mriauuuu~
mriaau~
mriaauu~
mriaauuu~
mriaauuuu~
mriaaau~
mriaaauu~
mriaaauuu~
mriaaauuuu~
mriiau~
mriiauu~
mriiauuu~
mriiauuuu~
mriiaau~
mriiaauu~
mriiaauuu~
mriiaauuuu~
mriiaaau~
mriiaaauu~
mriiaaauuu~
mriiaaauuuu~
mrau~
mrauu~
mrauuu~
mrauuuu~
mraau~
mraauu~
mraauuu~
mraauuuu~
mraaau~
mraaauu~
mraaauuu~
mraaauuuu~
miau~
miauu~
miauuu~
miauuuu~
miaau~
miaauu~
miaauuu~
miaauuuu~
miaaau~
miaaauu~
miaaauuu~
miaaauuuu~
miiau~
miiauu~
miiauuu~
miiauuuu~
miiaau~
miiaauu~
miiaauuu~
miiaauuuu~
miiaaau~
miiaaauu~
miiaaauuu~
miiaaauuuu~
mau~
mauu~
mauuu~
mauuuu~
maau~
maauu~
maauuu~
maauuuu~
maaau~
maaauu~
maaauuu~
maaauuuu~
nya
nyaa
nyaaa
nyaaaa
nyaaaaa
nyaaaaaa
nya~
nyaa~
nyaaa~
nyaaaa~
nyaaaaa~
nyaaaaaa~
mrp~
mrp
mrpp~
mrpp
mr~
mr
mrrp~
mrrp
mrrpp~
mrrpp
mrr~
mrr
mrrrp~
mrrrp
mrrrpp~
mrrrpp
mrrr~
mrrr
mrrrrp~
mrrrrp
mrrrrpp~
mrrrrpp
mrrrr~
mrrrr
mrrrrrp~
mrrrrrp
mrrrrrpp~
mrrrrrpp
mrrrrr~
mrrrrr

55
src/censorer.rs Normal file
View file

@ -0,0 +1,55 @@
use std::{fs, sync::LazyLock};
use aho_corasick::{AhoCorasick, AhoCorasickBuilder};
const CENSOR_CHAR: &str = "";
static BAD_WORDS: LazyLock<Option<AhoCorasick>> = LazyLock::new(|| {
let file = fs::read_to_string("bad_words.txt");
if let Ok(content) = file {
let pats: Vec<String> = content
.lines()
.filter(|ln| !ln.is_empty())
.map(|s| s.trim().to_owned())
.collect();
let aho_corasick = AhoCorasickBuilder::new()
.ascii_case_insensitive(true)
.build(pats)
.expect("ahocorasick should be built???");
Some(aho_corasick)
} else {
None
}
});
pub fn censor_message(message: impl Into<String>) -> Option<String> {
let Some(words) = &*BAD_WORDS else {
eprintln!("Wordsfile empty");
return None;
};
let message: String = message.into();
let mut modified = false;
let Some(ahocorasick) = &*BAD_WORDS else {
return None;
};
let mut out = String::with_capacity(message.len());
ahocorasick.replace_all_with(&message, &mut out, |_, word, dst| {
modified = true;
dst.push_str(
&(CENSOR_CHAR
.to_string()
.as_str()
.repeat(word.chars().count())),
);
true
});
modified.then_some(out)
}

28
src/hookidy.rs Normal file
View file

@ -0,0 +1,28 @@
use serenity::all::{Context, CreateAttachment, CreateWebhook, ExecuteWebhook, Message};
pub async fn replace_with_hooked(ctx: Context, msg: Message, new: String) -> serenity::Result<()> {
let author = &msg.author;
let name = msg
.member
.as_ref()
.and_then(|v| v.nick.clone())
.unwrap_or_else(|| author.display_name().to_string());
let mut hook = CreateWebhook::new(name);
if let Some(url) = author.avatar_url() {
let avatar_attachment = CreateAttachment::url(&ctx.http, &url).await?;
hook = hook.avatar(&avatar_attachment);
}
let hook = msg.channel_id.create_webhook(&ctx.http, hook).await?;
msg.delete(&ctx.http).await?;
let exec = ExecuteWebhook::new().content(new);
hook.execute(&ctx.http, false, exec).await?;
hook.delete(&ctx.http).await?;
Ok(())
}

64
src/main.rs Normal file
View file

@ -0,0 +1,64 @@
use std::cell::LazyCell;
use std::sync::LazyLock;
use std::{env, fs};
use rand::random_range;
use serenity::all::Http;
use serenity::async_trait;
use serenity::model::channel::Message;
use serenity::prelude::*;
use crate::censorer::censor_message;
use crate::hookidy::replace_with_hooked;
use crate::meower::Meower;
mod censorer;
mod hookidy;
mod meower;
struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
let content = &msg.content.trim().to_owned();
dbg!(content);
if msg.author.bot {
return;
}
if let Some(meower) = Meower::get()
&& meower.check_if_meow(content)
{
if let Err(why) = msg.channel_id.say(&ctx.http, meower.meow()).await {
eprintln!("Error sending message: {why:?}");
}
} else if let Some(censored) = censor_message(content) {
if let Err(why) = replace_with_hooked(ctx, msg, censored).await {
eprintln!("Failed to replace msg with hooked: {why}");
}
} else if msg.content.to_lowercase().contains("good bot") {
if let Err(why) = msg.react(&ctx.http, '🥺').await {
eprintln!("Failed to appreciate being called a good bot: {why}");
}
}
}
}
#[tokio::main]
async fn main() {
let token = fs::read_to_string("token").expect("File `token` should exist in PWD");
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
let mut client = Client::builder(token.trim(), intents)
.event_handler(Handler)
.await
.expect("Err creating client");
// Start listening for events by starting a single shard
if let Err(why) = client.start().await {
println!("Client error: {why:?}");
}
}

49
src/meower.rs Normal file
View file

@ -0,0 +1,49 @@
//! this rust smells of java and i find that hilarious
use std::{fs, ops::DerefMut, sync::LazyLock};
use rand::random_range;
static MEOWER: Meower = Meower {
meows: LazyLock::new(|| {
let file = fs::read_to_string("meows.txt");
if let Ok(content) = file {
Some(
content
.lines()
.filter(|ln| !ln.is_empty())
.map(|s| s.trim().to_owned())
.collect(),
)
} else {
None
}
}),
};
pub struct Meower {
meows: LazyLock<Option<Vec<String>>>,
}
impl Meower {
pub fn get() -> Option<&'static Self> {
MEOWER.meows.is_some().then_some(&MEOWER)
}
// this is rancid
pub fn meow(&self) -> String {
self.meows
.clone()
.map(|meows| meows[random_range(0..meows.len())].clone())
.clone()
.expect("meows existence should be checked on init")
.to_owned()
}
pub fn check_if_meow(&self, item: &str) -> bool {
if let Some(meows) = &*self.meows {
return meows.iter().any(|it| *it == item.trim().to_lowercase());
}
false
}
}