diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..7c48ece --- /dev/null +++ b/.env.template @@ -0,0 +1,13 @@ +TOKEN= +INSTANCE=BETA +OWNER_ID=784783517845946429 +XP_GAIN=1,1,1,2 +COOLDOWN=7,8,9,10 +CASH_BALANCE_NAME=$ +SPECIAL_BALANCE_NAME=majikoins +DBX_OAUTH2_REFRESH_TOKEN= +DBX_APP_KEY= +DBX_APP_SECRET= +MARIADB_USER= +MARIADB_PASSWORD= +MARIADB_ROOT_PASSWORD= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 15a4615..4e9278b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ __pycache__/ *.db .env -*.log \ No newline at end of file +*.log + +db/data/ +db/init/2-data.sql \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c659652 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM python:3.11 +ARG DEBIAN_FRONTEND=noninteractive + +WORKDIR /usr/src/app + +RUN apt-get update && \ + apt-get install -y locales && \ + sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ + dpkg-reconfigure locales + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +ENV LANG en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 + +CMD [ "python", "./main.py" ] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8a8ce3 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +This branch uses Docker and MariaDB to run Racu. It has the exact same functionality within Discord. +There is an ".env.template" file, please copy it to `.env` and fill out the details if you want to host Racu yourself. + +Beware that this branch is much more complicated than Racu when its ran locally with sqlite3, however it is more suitable if you plan to host Racu on a major scale with thousands or millions of Discord servers. \ No newline at end of file diff --git a/data/Birthday.py b/data/Birthday.py index 807dc68..f1dbe7d 100644 --- a/data/Birthday.py +++ b/data/Birthday.py @@ -13,7 +13,7 @@ class Birthday: query = """ INSERT OR REPLACE INTO birthdays (user_id, birthday) - VALUES (?, ?) + VALUES (%s, %s) """ database.execute_query(query, (self.user_id, birthday)) @@ -23,7 +23,7 @@ class Birthday: query = """ SELECT user_id FROM birthdays - WHERE strftime('%m-%d', birthday) = ? + WHERE strftime('%m-%d', birthday) = %s """ tz = pytz.timezone('US/Eastern') diff --git a/data/BlackJackStats.py b/data/BlackJackStats.py index 4dc5f78..3b2d120 100644 --- a/data/BlackJackStats.py +++ b/data/BlackJackStats.py @@ -15,7 +15,7 @@ class BlackJackStats: def push(self): query = """ INSERT INTO stats_bj (user_id, is_won, bet, payout, hand_player, hand_dealer) - VALUES (?, ?, ?, ?, ?, ?) + VALUES (%s, %s, %s, %s, %s, %s) """ values = (self.user_id, self.is_won, self.bet, self.payout, self.hand_player, self.hand_dealer) @@ -32,7 +32,7 @@ class BlackJackStats: SUM(CASE WHEN is_won = 1 THEN 1 ELSE 0 END) AS winning, SUM(CASE WHEN is_won = 0 THEN 1 ELSE 0 END) AS losing FROM stats_bj - WHERE user_id = ?; + WHERE user_id = %s; """ (amount_of_games, total_bet, total_payout, winning_amount, losing_amount) = database.select_query(query, (user_id,))[0] diff --git a/data/Currency.py b/data/Currency.py index 88d8cb3..6b16bbc 100644 --- a/data/Currency.py +++ b/data/Currency.py @@ -29,8 +29,8 @@ class Currency: def push(self): query = """ UPDATE currency - SET cash_balance = ?, special_balance = ? - WHERE user_id = ? + SET cash_balance = %s, special_balance = %s + WHERE user_id = %s """ database.execute_query(query, (round(self.cash), round(self.special), self.user_id)) @@ -40,7 +40,7 @@ class Currency: query = """ SELECT cash_balance, special_balance FROM currency - WHERE user_id = ? + WHERE user_id = %s """ try: @@ -54,7 +54,7 @@ class Currency: if cash_balance is None or special_balance is None: query = """ INSERT INTO currency (user_id, cash_balance, special_balance) - VALUES (?, 50, 3) + VALUES (%s, 50, 3) """ database.execute_query(query, (user_id,)) return 50, 3 diff --git a/data/Dailies.py b/data/Dailies.py index 8a413ed..bd12141 100644 --- a/data/Dailies.py +++ b/data/Dailies.py @@ -34,7 +34,7 @@ class Dailies: query = """ INSERT INTO dailies (user_id, amount, claimed_at, streak) - VALUES (?, ?, ?, ?) + VALUES (%s, %s, %s, %s) """ values = (self.user_id, self.amount, self.claimed_at, self.streak) database.execute_query(query, values) @@ -80,7 +80,7 @@ class Dailies: WHERE id = ( SELECT MAX(id) FROM dailies - WHERE user_id = ? + WHERE user_id = %s ) """ diff --git a/data/Inventory.py b/data/Inventory.py index bc320ed..4e0d3fb 100644 --- a/data/Inventory.py +++ b/data/Inventory.py @@ -21,7 +21,7 @@ class Inventory: query = """ INSERT OR REPLACE INTO inventory (user_id, item_id, quantity) - VALUES (?, ?, COALESCE((SELECT quantity FROM inventory WHERE user_id = ? AND item_id = ?), 0) + ?) + VALUES (%s, %s, COALESCE((SELECT quantity FROM inventory WHERE user_id = %s AND item_id = %s), 0) + %s) """ database.execute_query(query, (self.user_id, item.id, self.user_id, item.id, abs(quantity))) @@ -29,13 +29,13 @@ class Inventory: def take_item(self, item: Item.Item, quantity=1): query = """ INSERT OR REPLACE INTO inventory (user_id, item_id, quantity) - VALUES (?, ?, COALESCE((SELECT quantity FROM inventory WHERE user_id = ? AND item_id = ?) - ?, 0)) + VALUES (%s, %s, COALESCE((SELECT quantity FROM inventory WHERE user_id = %s AND item_id = %s) - %s, 0)) """ database.execute_query(query, (self.user_id, item.id, self.user_id, item.id, abs(quantity))) def get_inventory(self): - query = "SELECT item_id, quantity FROM inventory WHERE user_id = ? AND quantity > 0" + query = "SELECT item_id, quantity FROM inventory WHERE user_id = %s AND quantity > 0" results = database.select_query(query, (self.user_id,)) items_dict = {} @@ -47,7 +47,7 @@ class Inventory: return items_dict def get_item_quantity(self, item: Item.Item): - query = "SELECT COALESCE(quantity, 0) FROM inventory WHERE user_id = ? AND item_id = ?" + query = "SELECT COALESCE(quantity, 0) FROM inventory WHERE user_id = %s AND item_id = %s" result = database.select_query_one(query, (self.user_id, item.id)) return result @@ -57,7 +57,7 @@ class Inventory: FROM inventory JOIN ShopItem ON inventory.item_id = ShopItem.item_id JOIN item ON inventory.item_id = item.id - WHERE inventory.user_id = ? AND inventory.quantity > 0 AND ShopItem.worth > 0 + WHERE inventory.user_id = %s AND inventory.quantity > 0 AND ShopItem.worth > 0 """ try: diff --git a/data/Item.py b/data/Item.py index ce7d62c..8edf0d7 100644 --- a/data/Item.py +++ b/data/Item.py @@ -25,7 +25,7 @@ class Item: query = """ SELECT name, display_name, description, image_url, emote_id, quote, type FROM item - WHERE id = ? + WHERE id = %s """ data = database.select_query(query, (self.id,))[0] @@ -33,7 +33,7 @@ class Item: def get_quantity(self, author_id): query = """ - SELECT COALESCE((SELECT quantity FROM inventory WHERE user_id = ? AND item_id = ?), 0) AS quantity + SELECT COALESCE((SELECT quantity FROM inventory WHERE user_id = %s AND item_id = %s), 0) AS quantity """ quantity = database.select_query_one(query, (author_id, self.id)) @@ -44,7 +44,7 @@ class Item: query = """ SELECT worth FROM ShopItem - WHERE item_id = ? + WHERE item_id = %s """ return database.select_query_one(query, (self.id,)) @@ -66,7 +66,7 @@ class Item: query = """ INSERT OR REPLACE INTO item (id, name, display_name, description, image_url, emote_id, quote, type) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s) """ database.execute_query(query, (index, name, display_name, description, image_url, emote_id, quote, item_type)) @@ -92,12 +92,12 @@ class Item: @staticmethod def get_item_by_display_name(display_name): - query = "SELECT id FROM item WHERE display_name = ?" + query = "SELECT id FROM item WHERE display_name = %s" item_id = database.select_query_one(query, (display_name,)) return Item(item_id) @staticmethod def get_item_by_name(name): - query = "SELECT id FROM item WHERE name = ?" + query = "SELECT id FROM item WHERE name = %s" item_id = database.select_query_one(query, (name,)) return Item(item_id) diff --git a/data/ShopItem.py b/data/ShopItem.py index 55c2652..8180071 100644 --- a/data/ShopItem.py +++ b/data/ShopItem.py @@ -18,22 +18,22 @@ class ShopItem: """ def set_price(self, price): - query = "UPDATE ShopItem SET price = ? WHERE item_id = ?" + query = "UPDATE ShopItem SET price = %s WHERE item_id = %s" database.execute_query(query, (price, self.item.id)) self.price = price def set_price_special(self, price_special): - query = "UPDATE ShopItem SET price_special = ? WHERE item_id = ?" + query = "UPDATE ShopItem SET price_special = %s WHERE item_id = %s" database.execute_query(query, (price_special, self.item.id)) self.price_special = price_special def set_worth(self, worth): - query = "UPDATE ShopItem SET worth = ? WHERE item_id = ?" + query = "UPDATE ShopItem SET worth = %s WHERE item_id = %s" database.execute_query(query, (worth, self.item.id)) self.worth = worth def set_description(self, description): - query = "UPDATE ShopItem SET description = ? WHERE item_id = ?" + query = "UPDATE ShopItem SET description = %s WHERE item_id = %s" database.execute_query(query, (description, self.item.id)) self.description = description @@ -41,7 +41,7 @@ class ShopItem: query = """ SELECT price, price_special, worth, description FROM ShopItem - WHERE item_id = ? + WHERE item_id = %s """ try: @@ -49,7 +49,7 @@ class ShopItem: except (IndexError, TypeError): query = """ INSERT INTO ShopItem (item_id, price, price_special, worth, description) - VALUES (?, 0, 0, 0, "placeholder_descr") + VALUES (%s, 0, 0, 0, "placeholder_descr") """ database.execute_query(query, (self.item.id,)) (price, price_special, worth, description) = 0, 0, 0, "placeholder_descr" diff --git a/data/SlotsStats.py b/data/SlotsStats.py index 24f8e3d..7abc393 100644 --- a/data/SlotsStats.py +++ b/data/SlotsStats.py @@ -15,7 +15,7 @@ class SlotsStats: def push(self): query = """ INSERT INTO stats_slots (user_id, is_won, bet, payout, spin_type, icons) - VALUES (?, ?, ?, ?, ?, ?) + VALUES (%s, %s, %s, %s, %s, %s) """ values = (self.user_id, self.is_won, self.bet, self.payout, self.spin_type, self.icons) @@ -34,7 +34,7 @@ class SlotsStats: SUM(CASE WHEN spin_type = 'three_diamonds' AND is_won = 1 THEN 1 ELSE 0 END) AS games_won_three_diamonds, SUM(CASE WHEN spin_type = 'jackpot' AND is_won = 1 THEN 1 ELSE 0 END) AS games_won_jackpot FROM stats_slots - WHERE user_id = ? + WHERE user_id = %s """ (amount_of_games, total_bet, diff --git a/data/Xp.py b/data/Xp.py index b965def..98a3738 100644 --- a/data/Xp.py +++ b/data/Xp.py @@ -24,13 +24,13 @@ class Xp: def push(self): query = """ UPDATE xp - SET user_xp = ?, user_level = ?, cooldown = ? - WHERE user_id = ? + SET user_xp = %s, user_level = %s, cooldown = %s + WHERE user_id = %s """ database.execute_query(query, (self.xp, self.level, self.ctime, self.user_id)) def fetch_or_create_xp(self): - query = "SELECT user_xp, user_level, cooldown FROM xp WHERE user_id = ?" + query = "SELECT user_xp, user_level, cooldown FROM xp WHERE user_id = %s" try: (user_xp, user_level, cooldown) = database.select_query(query, (self.user_id,))[0] @@ -40,7 +40,7 @@ class Xp: if any(var is None for var in [user_xp, user_level, cooldown]): query = """ INSERT INTO xp (user_id, user_xp, user_level, cooldown) - VALUES (?, 0, 0, ?) + VALUES (%s, 0, 0, %s) """ database.execute_query(query, (self.user_id, time.time())) (user_xp, user_level, cooldown) = (0, 0, time.time()) diff --git a/db/database.py b/db/database.py index 1418d25..daca56c 100644 --- a/db/database.py +++ b/db/database.py @@ -1,22 +1,25 @@ import logging -import sqlite3 -from sqlite3 import Error +import mysql.connector +from mysql.connector import Error +from dotenv import load_dotenv +import os racu_logs = logging.getLogger('Racu.Core') +load_dotenv('.env') -def create_connection(): - try: - conn = sqlite3.connect("db/rcu.db") - except Error as e: - racu_logs.error("'create_connection()' Error occurred: {}".format(e)) - return - - return conn +cnxpool = mysql.connector.pooling.MySQLConnectionPool( + pool_name='core-pool', + pool_size=25, + host='db', + user=os.getenv("MARIADB_USER"), + password=os.getenv("MARIADB_PASSWORD"), + database='racudb' +) def execute_query(query, values=None): - conn = create_connection() + conn = cnxpool.get_connection() cursor = conn.cursor() if values: @@ -25,32 +28,42 @@ def execute_query(query, values=None): cursor.execute(query) conn.commit() + conn.close() racu_logs.debug(f"database.execute_query: 'query': {query}, 'values': {values}") return cursor def select_query(query, values=None): - conn = create_connection() + conn = cnxpool.get_connection() cursor = conn.cursor() racu_logs.debug(f"database.select_query: 'query': {query}, 'values': {values}") if values: - return cursor.execute(query, values).fetchall() + cursor.execute(query, values) + output = cursor.fetchall() else: - return cursor.execute(query).fetchall() + cursor.execute(query) + output = cursor.fetchall() + + conn.close() + return output def select_query_one(query, values=None): - conn = create_connection() + conn = cnxpool.get_connection() cursor = conn.cursor() racu_logs.debug(f"database.select_query_one: 'query': {query}, 'values': {values}") if values: - output = cursor.execute(query, values).fetchone() + cursor.execute(query, values) + output = cursor.fetchone() else: - output = cursor.execute(query).fetchone() + cursor.execute(query) + output = cursor.fetchone() + + conn.close() if output: return output[0] diff --git a/db/init/1-tables.sql b/db/init/1-tables.sql new file mode 100644 index 0000000..73aa5a3 --- /dev/null +++ b/db/init/1-tables.sql @@ -0,0 +1,82 @@ +SET FOREIGN_KEY_CHECKS=0; + +CREATE TABLE xp ( + user_id BIGINT PRIMARY KEY NOT NULL, + user_xp INT NOT NULL, + user_level INT NOT NULL, + cooldown FLOAT +); + +CREATE TABLE currency ( + user_id BIGINT PRIMARY KEY NOT NULL, + cash_balance BIGINT NOT NULL, + special_balance BIGINT +); + +CREATE TABLE stats_bj ( + id INT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT, + is_won BOOLEAN, + bet BIGINT, + payout BIGINT, + hand_player TEXT, + hand_dealer TEXT +); + +CREATE TABLE stats_slots ( + id INT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT, + is_won BOOLEAN, + bet BIGINT, + payout BIGINT, + spin_type TEXT, + icons TEXT +); + +CREATE TABLE stats_duel ( + id INT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT, + is_won BOOLEAN, + bet BIGINT +); + +CREATE TABLE dailies ( + id INT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT, + amount BIGINT, + claimed_at TINYTEXT, + streak INT +); + +CREATE TABLE item ( + id INT PRIMARY KEY AUTO_INCREMENT, + name TEXT, + display_name TEXT, + description TEXT, + image_url TEXT, + emote_id BIGINT, + quote TEXT, + type TEXT +); + +CREATE TABLE inventory ( + user_id BIGINT, + item_id INT, + quantity INT, + + PRIMARY KEY (user_id, item_id), + FOREIGN KEY (item_id) REFERENCES item (id) +); + +CREATE TABLE ShopItem ( + item_id INT PRIMARY KEY, + price BIGINT, + price_special BIGINT, + worth BIGINT, + description TEXT +); + +CREATE TABLE birthdays ( + user_id BIGINT NOT NULL PRIMARY KEY, + birthday DATETIME DEFAULT NULL +); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9a0a487 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3' + +services: + core: + build: . + restart: always + depends_on: + db: + condition: service_healthy + + db: + image: mariadb + restart: always + environment: + MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD} + MARIADB_USER: ${MARIADB_USER} + MARIADB_PASSWORD: ${MARIADB_PASSWORD} + MARIADB_DATABASE: racudb + volumes: + - ./db/init:/docker-entrypoint-initdb.d/ + - ./db/data:/var/lib/mysql + ports: + - 3306:3306 + healthcheck: + test: [ "CMD", "mariadb", "-h", "localhost", "-u", "${MARIADB_USER}", "-p${MARIADB_PASSWORD}", "-e", "SELECT 1" ] + interval: 10s + timeout: 10s + retries: 5 + + adminer: + image: adminer + restart: always + ports: + - 8080:8080 diff --git a/main.py b/main.py index dc24a29..03d5f72 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,9 @@ SPECIAL_BALANCE_NAME= DBX_OAUTH2_REFRESH_TOKEN= DBX_APP_KEY= DBX_APP_SECRET= +MARIADB_USER= +MARIADB_PASSWORD= +MARIADB_ROOT_PASSWORD= """ import logging @@ -280,8 +283,8 @@ if __name__ == '__main__': load_dotenv('.env') # load db - db.tables.sync_database() - Item.insert_items() + # db.tables.sync_database() + # Item.insert_items() load_cogs() sbbot.run(os.getenv('TOKEN')) diff --git a/requirements.txt b/requirements.txt index 2b57fea..3fb396c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ py-cord==2.4.1 python-dotenv==1.0.0 setuptools==67.8.0 pytz==2023.3 -dropbox==11.36.2 \ No newline at end of file +dropbox==11.36.2 +mysql-connector-python==8.1.0 \ No newline at end of file