diff --git a/POLICY.md b/POLICY.md index 392df10..12c862b 100644 --- a/POLICY.md +++ b/POLICY.md @@ -7,14 +7,10 @@ Without storing some aspects of your user data, the bot would not be able to run We collect the following: -* User IDs +* User IDs and Guild IDs Required for functionality of the blacklist command. -* Guild/Channel ID pairs - - Required for the functionality of the announcements and announcements command. - * Messages Messages are not stored, but they are used for the main functionality of cheeseBot. diff --git a/src/bot.rb b/src/bot.rb index 4a2f0b7..988815e 100644 --- a/src/bot.rb +++ b/src/bot.rb @@ -16,18 +16,31 @@ require 'discordrb' require 'securerandom' +require_relative 'events/message_check' class CheeseBot def initialize + puts "Initializing bot..." @bot = Discordrb::Bot.new( token: ENV['BOT_TOKEN'], - intents: [:servers, :server_messages] + intents: [:servers, :server_messages], ) - + + puts "Registering the Message Checker..." + MessageCheck.register(@bot) + + puts "Initializing database..." + Database.setup + + puts "Loading commands..." load_commands + + puts "Starting bot..." start_bot end + + def run @bot.run end diff --git a/src/commands/not_cheese_related/blacklist.rb b/src/commands/not_cheese_related/blacklist.rb new file mode 100644 index 0000000..06717d6 --- /dev/null +++ b/src/commands/not_cheese_related/blacklist.rb @@ -0,0 +1,60 @@ +# cheeseBot +# Copyright (C) 2026 Eri (csxkdv/nxkdv) nxkdv@thenight.club +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +require_relative '../../modules/database' + +module Commands + module Blacklist + extend self + + def register(bot) + cmd_key = :blacklist + cmd_desc = "Blacklist yourself from the trigger DMs (using the command again will whitelist you.)" + + bot.register_application_command(cmd_key, cmd_desc, server_id: ENV['SERVER_ID']) + + bot.application_command(cmd_key) do |event| + added = Database.toggle_user_blacklist(event.user.id) + + if added + event.respond(content: "You have been added to the blacklist. You will no longer receive DMs.") + else + event.respond(content: "You have been removed from the blacklist. You will now receive DMs.") + end + end + + cmd_key_server = :server_blacklist + cmd_desc_server = "Blacklist the server from the trigger DMs (using the command again will whitelist the server.)" + + bot.register_application_command(cmd_key_server, cmd_desc_server, server_id: ENV['SERVER_ID']) + + bot.application_command(cmd_key_server) do |event| + unless event.user.permission?(:administrator) + event.respond(content: "You're not an admin. You can't use this command.", ephemeral: true) + next + end + + added = Database.toggle_server_blacklist(event.server.id) + + if added + event.respond(content: "Server has been added to the blacklist. This server will no longer receive DMs.") + else + event.respond(content: "Server has been removed from the blacklist. This server will now receive DMs.") + end + end + end + end +end \ No newline at end of file diff --git a/src/events/message_check.rb b/src/events/message_check.rb new file mode 100644 index 0000000..ca5f148 --- /dev/null +++ b/src/events/message_check.rb @@ -0,0 +1,50 @@ +# cheeseBot +# Copyright (C) 2026 Eri (csxkdv/nxkdv) nxkdv@thenight.club +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +require_relative '../modules/cheese_checker' +require_relative '../modules/database' + +module MessageCheck + def self.register(bot) + bot.message do |event| + next if event.user.bot_account || event.server.nil? + + cheese_content = CheeseChecker.check_content(event.content) + next unless cheese_content + + event.message.react("🧀") + + next if Database.server_blacklisted?(event.server.id) + next if Database.user_blacklisted?(event.user.id) + + begin + event.user.pm.send_embed do |embed| + embed.title = "Cheese Detected!" + embed.description = cheese_content + + embed.color = rand(0xFFFFFF) + embed.timestamp = Time.now + + embed.footer = Discordrb::Webhooks::EmbedFooter.new( + text: "Use /blacklist to not receive DMs anymore" + ) + end + rescue Discordrb::Errors::NoPermission + puts "Could not DM user #{event.user.username} (#{event.user.id})" + end + end + end +end \ No newline at end of file diff --git a/src/lists/cheese_words.rb b/src/lists/cheese_words.rb new file mode 100644 index 0000000..fbbd474 --- /dev/null +++ b/src/lists/cheese_words.rb @@ -0,0 +1,86 @@ +module Lists + CHEESE_WORDS = [ + "abertam", + "abondance", + "acapella", + "accasciato", + "ackawi", + "acorn", + "adelost", + "backstein", + "bergader", + "bonifaz", + "brie", + "burrata", + "butterkäse", + "cambozola", + "camembert", + "caseus", + "casgiu", + "cheddar", + "cheese", + "chiiz", + "chisi", + "chizi", + "colby", + "cottage", + "cuku", + "cáis", + "djathë", + "edelpilzkäse", + "farmaajo", + "feta", + "formaggio", + "formatge", + "fromage", + "fromazy", + "fromaĝo", + "fwomaj", + "gazta", + "gorgonzola", + "gouda", + "halloumi", + "handkäse", + "harzer", + "hirtenkäse", + "hohenheim", + "juust", + "juusto", + "kaas", + "kashkaval", + "keju", + "keso", + "kochkäse", + "käse", + "limburger", + "milbenkäse", + "mozzarella", + "nieheimer", + "obatzda", + "ostur", + "paneer", + "parmesan", + "pendir", + "penêr", + "pepperjack", + "provolone", + "quark", + "queijo", + "queixo", + "queso", + "raclette", + "rauchkäse", + "ricotta", + "roquefort", + "sajt", + "sires", + "sisi", + "spundekäs", + "swiss", + "sūris", + "tsiis", + "ziegel", + "チーズ", + "\N{CHEESE WEDGE}" + ].freeze +end \ No newline at end of file diff --git a/src/modules/cheese_checker.rb b/src/modules/cheese_checker.rb new file mode 100644 index 0000000..1366b72 --- /dev/null +++ b/src/modules/cheese_checker.rb @@ -0,0 +1,38 @@ +# cheeseBot +# Copyright (C) 2026 Eri (csxkdv/nxkdv) nxkdv@thenight.club +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +require_relative '../lists/cheese_words' + +module CheeseChecker + WORDS_LIST = Lists::CHEESE_WORDS + PUNCTUATION = "!\"£$%^&*()[]{}'@#~;:,<.>/?-+\\|`¬" + + NO_CHEESE = /[\s#{Regexp.escape(PUNCTUATION)}]*/ + + pattern_strings = WORDS_LIST.map do |word| + Regexp.escape(word).chars.map { |char| char + NO_CHEESE.source }.join + end + + CHEESE_REGEX = /((?:#{pattern_strings.join('|')}))/im + + def self.check_content(content) + changed_content = content.gsub(CHEESE_REGEX, "**\\1**") + + return nil if changed_content == content + + changed_content + end +end \ No newline at end of file diff --git a/src/modules/database.rb b/src/modules/database.rb new file mode 100644 index 0000000..98b464b --- /dev/null +++ b/src/modules/database.rb @@ -0,0 +1,118 @@ +# cheeseBot +# Copyright (C) 2026 Eri (csxkdv/nxkdv) nxkdv@thenight.club +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +require 'pg' + +module Database + DB_NAME = ENV['DB_NAME'] + DB_USER = ENV['DB_USER'] + DB_HOST = ENV['DB_HOST'] + DB_PASSWORD = ENV['DB_PASSWORD'] + + def self.setup + create_database_if_not_exists + create_tables + end + + def self.connect(db_name = DB_NAME) + PG.connect( + dbname: db_name, + user: DB_USER, + host: DB_HOST, + password: DB_PASSWORD + ) + rescue PG::Error => e + puts "Database connection error: #{e.message}" + exit + end + + def self.user_blacklisted?(user_id) + exists?("blacklist", user_id) + end + + def self.server_blacklisted?(server_id) + exists?("server_blacklist", server_id) + end + + def self.toggle_user_blacklist(user_id) + toggle("blacklist", user_id) + end + + def self.toggle_server_blacklist(server_id) + toggle("server_blacklist", server_id) + end + + private + + def self.exists?(table, id) + conn = connect + result = conn.exec_params("SELECT 1 FROM #{table} WHERE id = $1", [id]) + conn.close + !result.ntuples.zero? + end + + def self.toggle(table, id) + conn = connect + + if exists?(table, id) + conn.exec_params("DELETE FROM #{table} WHERE id = $1", [id]) + conn.close + return false + else + conn.exec_params("INSERT INTO #{table} (id) VALUES ($1)", [id]) + conn.close + return true + end + end + + def self.create_database_if_not_exists + begin + conn = connect('postgres') + rescue PG::Error + puts "Could not connect to 'postgres' system database. Please create '#{DB_NAME}' manually." + return + end + + result = conn.exec_params("SELECT 1 FROM pg_database WHERE datname = $1", [DB_NAME]) + + if result.ntuples.zero? + puts "Database '#{DB_NAME}' not found. Creating it..." + conn.exec("CREATE DATABASE \"#{DB_NAME}\"") + puts "Database '#{DB_NAME}' created successfully!" + end + conn.close + end + + def self.create_tables + conn = connect + + conn.exec(<<~SQL) + CREATE TABLE IF NOT EXISTS blacklist ( + id BIGINT PRIMARY KEY + ); + SQL + + conn.exec(<<~SQL) + CREATE TABLE IF NOT EXISTS server_blacklist ( + id BIGINT PRIMARY KEY + ); + SQL + + puts "Tables verified." + conn.close + end +end + \ No newline at end of file