Skip to main content
Version: Latest

SQL Database (db)

The primary, persistent storage. SQLite database is used.

Each module has its own database (and each account has its own). These databases are located at /databases/<ModuleName>/database_<user_id>.db. This path can be obtained using mod.db_path.

Methods

Set-Get System

Data is stored in a key-value format. Only simple data types and structures can be stored: str, int, float, bool, None, list, dict.

info

Below, the term Any refers to the aforementioned simple data types and structures.

  • async set

    Sets a value.

    Arguments:

    • variable (str) - Name of the variable (key).
    • value (Any) - Value.

    Returns: None

    Example:

    await mod.db.set('count', 10)
  • async get

    Retrieves a value. If no record with the specified key exists, default is returned.

    Arguments:

    • variable (str) - Name of the variable (key).
    • default (optional) = None - Default value to return if variable is not found.

    Returns: Any - The value of the variable.

    Example:

    count = await mod.db.get('count', 0)
  • async getall

    Retrieves all values.

    Arguments:

    • default (optional) = None - Default value to return if no variables exist in the database.

    Returns: List[str, Any] - A dictionary of values where keys are variable names.

    Example:

    all_values = await mod.db.getall()
  • async remove / delete

    Deletes a variable from the database.

    Arguments:

    • variable (str) - Name of the variable to delete.

    Returns: None

    Example:

    await mod.db.remove('count')

SQL

In addition to set-get, you can use the SQL database directly via SQL queries!

  • async sql

    Executes an SQL query.

    Arguments:

    • sql (str) - SQL query.
    • asdict (optional, bool) = False - Return results as a dictionary (column: value).
    • **params - Values to pass into the SQL query.
    danger

    Always use query parameters (**params) for value substitution!

    Do not insert data directly using f-strings or +, as this opens vulnerabilities for SQL injection.

    Correct:

    await mod.db.sql("SELECT * FROM users WHERE age > :age", age=18)

    Incorrect:

    await mod.db.sql(f"SELECT * FROM users WHERE age > {age}")  # Vulnerable!!!!

    Passing parameters via **params automatically protects against attacks and ensures code safety.

    Returns: None | Tuple[Tuple[Any]] | Dict[str, Any] - The result of the SQL query.

    info

    If the query returns data, the output will be as follows:

    • With asdict=True: [{column: value, ...}, ...]
    • With asdict=False: [(value1, value2, ...), ...]

    Example:

    result = await mod.db.sql("SELECT id, name FROM users WHERE age > :age", asdict=True, age=18)
    # Example result: [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]

Examples:

info

The following examples are taken from the MineEvoMiner module (version 2.5) by third-party developer @Kotcananacom.

MineEvoMiner/miner/mine.py, lines 25-42
# When the client is running
async def stats_ready(app: Client, mod: Module):
AutoStopTimer(app, mod)
mod.add_task(miner(app, mod))
if await mod.db.get('mine', False):
await mod.db.set('start_mine', time.time())
await mod.db.sql("CREATE TABLE IF NOT EXISTS mineevo_stats (type_case TEXT, type_bust TEXT, count INTEGER, timee TEXT)")
await initialization_found(app, mod)


# Enable/disable beautiful formatting for found cases
async def beautiful_case_found(app: Client, msg: Message, mod: Module):
beautiful = await mod.db.get('beautiful_found', False)
await mod.db.set('beautiful_found', not beautiful)
if beautiful:
await msg.edit(f"{Emojis.YES_2} {b('Beautiful formatting for found cases disabled')}")
else:
await msg.edit(f"{Emojis.YES} {b('Beautiful formatting for found cases enabled')}")
MineEvoMiner/miner/mine.py, lines 161-177
# Enable/disable mining
async def mine_cmd(app: Client, msg: Message, mod: Module):
mine = await mod.db.get('mine', False)
clicks = await mod.db.get('clicks', 0)
get_mine_bot_value = await get_mine_bot(mod)
await mod.db.set('mine', not mine)
if mine:
await mod.db.set('end_mine', time.time())
start = await mod.db.get('start_mine', 0)
end = await mod.db.get('end_mine', 0)
if end > start:
session_time = end - start
total_time = await mod.db.get('mine_total_time', 0)
total_time += session_time
await mod.db.set('mine_total_time', total_time)
cases = await mod.db.sql("SELECT type_case, SUM(count) FROM mineevo_stats WHERE timee BETWEEN :start_time AND :end_time GROUP BY type_case", start_time=start, end_time=end)
busts = await mod.db.sql("SELECT type_bust, SUM(count) FROM mineevo_stats WHERE timee BETWEEN :start_time AND :end_time GROUP BY type_bust", start_time=start, end_time=end)