From ac90a9628d090375f3f1c41a8ba47beda0527d06 Mon Sep 17 00:00:00 2001 From: csxkdv Date: Mon, 5 Jan 2026 14:36:22 -0300 Subject: [PATCH] Main commit --- .gitignore | 8 +++ Gemfile | 5 ++ Gemfile.lock | 121 +++++++++++++++++++++++++++++++++++++++ main.rb | 8 +++ src/bot.rb | 58 +++++++++++++++++++ src/commands/currency.rb | 20 +++++++ src/commands/echo.rb | 14 +++++ src/commands/ping.rb | 13 +++++ src/database.rb | 36 ++++++++++++ 9 files changed, 283 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 main.rb create mode 100644 src/bot.rb create mode 100644 src/commands/currency.rb create mode 100644 src/commands/echo.rb create mode 100644 src/commands/ping.rb create mode 100644 src/database.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee8278c --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Ignore Bundler config +.bundle/ + +# Ignore locally installed gems/dependencies +vendor/ + +# Ignore environment variables (sensitive data) +.env \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..37b2f14 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'discordrb', git: 'https://github.com/shardlab/discordrb.git', branch: 'main' +gem 'dotenv' +gem 'pg' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..5700f92 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,121 @@ +GIT + remote: https://github.com/shardlab/discordrb.git + revision: 29fddad8a2861f8d96d882b6f8d8a8cf062972e2 + branch: main + specs: + discordrb (3.7.2) + base64 (~> 0.2) + discordrb-webhooks (~> 3.7.2) + ffi (>= 1.9.24) + opus-ruby + rest-client (>= 2.0.0) + websocket-client-simple (>= 0.9.0) + discordrb-webhooks (3.7.2) + rest-client (>= 2.0.0) + +GEM + remote: https://rubygems.org/ + specs: + base64 (0.3.0) + domain_name (0.6.20240107) + dotenv (3.2.0) + event_emitter (0.2.6) + ffi (1.17.3) + ffi (1.17.3-aarch64-linux-gnu) + ffi (1.17.3-aarch64-linux-musl) + ffi (1.17.3-arm-linux-gnu) + ffi (1.17.3-arm-linux-musl) + ffi (1.17.3-arm64-darwin) + ffi (1.17.3-x86-linux-gnu) + ffi (1.17.3-x86-linux-musl) + ffi (1.17.3-x86_64-darwin) + ffi (1.17.3-x86_64-linux-gnu) + ffi (1.17.3-x86_64-linux-musl) + http-accept (1.7.0) + http-cookie (1.1.0) + domain_name (~> 0.5) + logger (1.7.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0924) + mutex_m (0.3.0) + netrc (0.11.0) + opus-ruby (1.0.1) + ffi + pg (1.6.3) + pg (1.6.3-aarch64-linux) + pg (1.6.3-aarch64-linux-musl) + pg (1.6.3-arm64-darwin) + pg (1.6.3-x86_64-darwin) + pg (1.6.3-x86_64-linux) + pg (1.6.3-x86_64-linux-musl) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + websocket (1.2.11) + websocket-client-simple (0.9.0) + base64 + event_emitter + mutex_m + websocket + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + ruby + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + discordrb! + dotenv + pg + +CHECKSUMS + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + discordrb (3.7.2) + discordrb-webhooks (3.7.2) + domain_name (0.6.20240107) sha256=5f693b2215708476517479bf2b3802e49068ad82167bcd2286f899536a17d933 + dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d + event_emitter (0.2.6) sha256=c72697bd5cce9d36594be1972c17f1c9a573236f44303a4d1d548080364e1391 + ffi (1.17.3) sha256=0e9f39f7bb3934f77ad6feab49662be77e87eedcdeb2a3f5c0234c2938563d4c + ffi (1.17.3-aarch64-linux-gnu) sha256=28ad573df26560f0aedd8a90c3371279a0b2bd0b4e834b16a2baa10bd7a97068 + ffi (1.17.3-aarch64-linux-musl) sha256=020b33b76775b1abacc3b7d86b287cef3251f66d747092deec592c7f5df764b2 + ffi (1.17.3-arm-linux-gnu) sha256=5bd4cea83b68b5ec0037f99c57d5ce2dd5aa438f35decc5ef68a7d085c785668 + ffi (1.17.3-arm-linux-musl) sha256=0d7626bb96265f9af78afa33e267d71cfef9d9a8eb8f5525344f8da6c7d76053 + ffi (1.17.3-arm64-darwin) sha256=0c690555d4cee17a7f07c04d59df39b2fba74ec440b19da1f685c6579bb0717f + ffi (1.17.3-x86-linux-gnu) sha256=868a88fcaf5186c3a46b7c7c2b2c34550e1e61a405670ab23f5b6c9971529089 + ffi (1.17.3-x86-linux-musl) sha256=f0286aa6ef40605cf586e61406c446de34397b85dbb08cc99fdaddaef8343945 + ffi (1.17.3-x86_64-darwin) sha256=1f211811eb5cfaa25998322cdd92ab104bfbd26d1c4c08471599c511f2c00bb5 + ffi (1.17.3-x86_64-linux-gnu) sha256=3746b01f677aae7b16dc1acb7cb3cc17b3e35bdae7676a3f568153fb0e2c887f + ffi (1.17.3-x86_64-linux-musl) sha256=086b221c3a68320b7564066f46fed23449a44f7a1935f1fe5a245bd89d9aea56 + http-accept (1.7.0) sha256=c626860682bfbb3b46462f8c39cd470fd7b0584f61b3cc9df5b2e9eb9972a126 + http-cookie (1.1.0) sha256=38a5e60d1527eebc396831b8c4b9455440509881219273a6c99943d29eadbb19 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56 + mime-types-data (3.2025.0924) sha256=f276bca15e59f35767cbcf2bc10e023e9200b30bd6a572c1daf7f4cc24994728 + mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751 + netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f + opus-ruby (1.0.1) sha256=8dc808d6773a488469374cccf252808b2ad7bc7e00229a319832f0e09012ce53 + pg (1.6.3) sha256=1388d0563e13d2758c1089e35e973a3249e955c659592d10e5b77c468f628a99 + pg (1.6.3-aarch64-linux) sha256=0698ad563e02383c27510b76bf7d4cd2de19cd1d16a5013f375dd473e4be72ea + pg (1.6.3-aarch64-linux-musl) sha256=06a75f4ea04b05140146f2a10550b8e0d9f006a79cdaf8b5b130cde40e3ecc2c + pg (1.6.3-arm64-darwin) sha256=7240330b572e6355d7c75a7de535edb5dfcbd6295d9c7777df4d9dddfb8c0e5f + pg (1.6.3-x86_64-darwin) sha256=ee2e04a17c0627225054ffeb43e31a95be9d7e93abda2737ea3ce4a62f2729d6 + pg (1.6.3-x86_64-linux) sha256=5d9e188c8f7a0295d162b7b88a768d8452a899977d44f3274d1946d67920ae8d + pg (1.6.3-x86_64-linux-musl) sha256=9c9c90d98c72f78eb04c0f55e9618fe55d1512128e411035fe229ff427864009 + rest-client (2.1.0) sha256=35a6400bdb14fae28596618e312776c158f7ebbb0ccad752ff4fa142bf2747e3 + websocket (1.2.11) sha256=b7e7a74e2410b5e85c25858b26b3322f29161e300935f70a0e0d3c35e0462737 + websocket-client-simple (0.9.0) sha256=f9a37c5e4922b35a711e21e6d73ed1e25892efa47d183203ab2f5beb4e563109 + +BUNDLED WITH + 4.0.3 diff --git a/main.rb b/main.rb new file mode 100644 index 0000000..4bdcca0 --- /dev/null +++ b/main.rb @@ -0,0 +1,8 @@ +require 'dotenv/load' +require_relative 'src/bot' + +# Instantiate the class +bot = FrugalityBot.new + +# Run it +bot.run \ No newline at end of file diff --git a/src/bot.rb b/src/bot.rb new file mode 100644 index 0000000..d685d29 --- /dev/null +++ b/src/bot.rb @@ -0,0 +1,58 @@ +require 'discordrb' +require_relative 'database' + +class FrugalityBot + def initialize + @bot = Discordrb::Bot.new( + token: ENV['BOT_TOKEN'], + intents: [:servers, :server_messages] + ) + + @db = Database.new + + load_commands + setup_events + end + + def run + @bot.run + end + + private + + def load_commands + # 1. We look for all .rb files in "src/commands/..." + comm_files = Dir[File.join(__dir__, 'commands', '*.rb')] + + comm_files.each do |file| + require file # We import the file + + # We convert filename to module name + # This mean that 'echo.rb' turns into 'Echo' + # 'server_info' would turn into 'ServerInfo' + filename = File.basename(file, '.rb') + module_name = filename.split('_').map(&:capitalize).join + + begin + # We find the module inside 'Commands' namespace + comm_module = Commands.const_get(module_name) + + # Register the command + comm_module.register(@bot, @db) + puts "Loaded command: #{module_name}" + rescue NameError => e + puts "Could not load #{filename}: Module 'Commands::#{module_name}' was not found." + rescue StandardError => e + puts "Error loading: #{filename}: #{e.message}" + end + end + puts "Commands loaded." + end + + def setup_events + @bot.ready do + puts "#{@bot.profile.username} is online" + @bot.update_status("online", "Checking the economy...", nil, 0, false, 0) + end + end +end \ No newline at end of file diff --git a/src/commands/currency.rb b/src/commands/currency.rb new file mode 100644 index 0000000..428cb3b --- /dev/null +++ b/src/commands/currency.rb @@ -0,0 +1,20 @@ +module Commands + module Currency + extend self + + def register(bot, db) + bot.register_application_command(:currency, 'Get your currency', server_id: ENV['TEST_SERVER_ID']) + + bot.application_command(:currency) do |event| + # 1. Get the User ID from the event + user_id = event.user.id + + # 2. Ask the DB for the balance + balance = db.get_currency(user_id) + + # 3. Respond to Discord + event.respond(content: "You have #{balance} coins.") + end + end + end +end \ No newline at end of file diff --git a/src/commands/echo.rb b/src/commands/echo.rb new file mode 100644 index 0000000..387715b --- /dev/null +++ b/src/commands/echo.rb @@ -0,0 +1,14 @@ +module Commands + module Echo + extend self + def register(bot, _db) + bot.register_application_command(:echo, 'Repeats what you say', server_id: ENV['TEST_SERVER_ID']) do |cmd| + cmd.string('message', 'The text you want the bot to repeat', required: true) + end + + bot.application_command(:echo) do |event| + event.respond(content: "You said: #{event.options['message']}") + end + end + end +end \ No newline at end of file diff --git a/src/commands/ping.rb b/src/commands/ping.rb new file mode 100644 index 0000000..78f7773 --- /dev/null +++ b/src/commands/ping.rb @@ -0,0 +1,13 @@ +module Commands + module Ping + extend self + + def register(bot, _db) + bot.register_application_command(:ping, 'Check if the bot is alive') + + bot.application_command(:ping) do |event| + event.respond(content: 'Pong!') + end + end + end +end \ No newline at end of file diff --git a/src/database.rb b/src/database.rb new file mode 100644 index 0000000..d6cc50e --- /dev/null +++ b/src/database.rb @@ -0,0 +1,36 @@ +require 'pg' + +class Database + def initialize + # Connect once when the bot starts + @conn = PG.connect(dbname: 'frugality_database') + + init_tables + end + + def init_tables + sql = <<~SQL + CREATE TABLE IF NOT EXISTS total_money ( + user_id BIGINT PRIMARY KEY, + amount BIGINT DEFAULT 0 + ); + SQL + + @conn.exec(sql) + puts "Database tables have been initialized." + end + + # We pass the user_id + def get_currency(user_id) + # 1. Run the query using parameters ($1) to prevent SQL injection + result = @conn.exec_params("SELECT amount FROM total_money WHERE user_id = $1", [user_id]) + + # 2. Check if the user exists + if result.num_tuples.zero? + return 0 # User has no money/row yet + else + # 3. Return the value (don't print it) + return result[0]['amount'].to_i + end + end +end \ No newline at end of file