Corsair - Auto Market Purchase

Newbie Spellweaver
Joined
Nov 24, 2023
Messages
20
Reaction score
20
Greetings. I played solo, so I wanted a more realistic Central Market feel.
I decided to make a small program that will purchase all of my items off of the marketplace, for a percentage, and then mail me the proceeds.


1. Install python. I use 3.9.5 which is installed when you install Corsair.
2. Use Task Scheduler to call on this script as often as you like.
3. No need for players to be logged out. When they open the market, they will see what was sold but cannot interact with it. When they list anything, the list will update to show nothing but what they just listed.
4. Adjust the code to change the % that it mails the player.
5. This requires sendMail which can be found in my post, here, about the Central/Black Market that I made (about half way down the page is instructions on send mail.

NOTES:
1. TAX_RATE can be adjusted. This is what is deducted from the purchase.
2. Silver item key is used to tell what gets sent to the player.
3. Sender_User_No = 1 (This is the admin account that I use to populate the market. I dont want to purchase all of the default items. I will post how to fill up the market next).

Python:
import pandas as pd
import pyodbc
import subprocess
import os

# --- Configuration ---
SERVER_TRADE = 'localhost'
DATABASE_TRADE = 'SA_BETA_TRADEDB_0002'

SERVER_WORLD = 'localhost'
DATABASE_WORLD = 'SA_BETA_WORLDDB_0002'

TAX_RATE = 0.15
SILVER_ITEM_KEY = 1
SENDER_USER_NO = 1

def get_users_with_market_items():
    conn_str = f'DRIVER={{ODBC Driver 17 for SQL Server}};SERVER={SERVER_TRADE};DATABASE={DATABASE_TRADE};Trusted_Connection=yes'
    with pyodbc.connect(conn_str) as conn:
        cursor = conn.cursor()
        cursor.execute("""
            SELECT DISTINCT _userNo
            FROM [PaGamePrivate].[TblWorldMarketBiddingSell]
            WHERE _userNo != 1
        """)
        return [row[0] for row in cursor.fetchall()]

def purchase_and_mail_items(target_user_no):
    cnxn_trade = None
    cnxn_world = None
    
    try:
        # --- Connect to Trade DB ---
        conn_str_trade = f'DRIVER={{ODBC Driver 17 for SQL Server}};SERVER={SERVER_TRADE};DATABASE={DATABASE_TRADE};Trusted_Connection=yes'
        cnxn_trade = pyodbc.connect(conn_str_trade)
        cursor_trade = cnxn_trade.cursor()
        cnxn_trade.autocommit = False
        print(f"\n--- Processing _userNo={target_user_no} ---")
        print(f"Connected to Trade DB '{DATABASE_TRADE}'.")

        select_query = """
        SELECT _sellNo, _mainKey, _subKey, _leftCount, _pricePerOne
        FROM [PaGamePrivate].[TblWorldMarketBiddingSell]
        WHERE _userNo = ?;
        """
        cursor_trade.execute(select_query, target_user_no)
        items_to_process = cursor_trade.fetchall()

        if not items_to_process:
            print("No market items. Skipping.")
            cnxn_trade.rollback()
            return False

        total_sale_value = 0
        deleted_sell_nos = []
        for item in items_to_process:
            sell_no, main_key, sub_key, left_count, price_per_one = item
            item_total = left_count * price_per_one
            total_sale_value += item_total
            deleted_sell_nos.append(sell_no)
            print(f"  - Item: {main_key}, Qty: {left_count}, Price: {price_per_one}, Total: {item_total}")

        tax_amount = int(total_sale_value * TAX_RATE)
        net_silver_to_send = total_sale_value - tax_amount
        print(f"  Total: {total_sale_value}, Tax: {tax_amount}, Net: {net_silver_to_send}")

        delete_placeholders = ', '.join(['?'] * len(deleted_sell_nos))
        delete_query = f"DELETE FROM [PaGamePrivate].[TblWorldMarketBiddingSell] WHERE _sellNo IN ({delete_placeholders});"
        cursor_trade.execute(delete_query, *deleted_sell_nos)
        cnxn_trade.commit()
        print("Items deleted.")

        if net_silver_to_send <= 0:
            print("No silver to send. Skipping mail.")
            return True

        # --- Get user nickname ---
        conn_str_world = f'DRIVER={{ODBC Driver 17 for SQL Server}};SERVER={SERVER_WORLD};DATABASE={DATABASE_WORLD};Trusted_Connection=yes'
        cnxn_world = pyodbc.connect(conn_str_world)
        cursor_world = cnxn_world.cursor()
        cursor_world.execute("SELECT _userNickname FROM [PaGamePrivate].[TblUserInformation] WHERE _userNo = ?;", target_user_no)
        row = cursor_world.fetchone()

        if not row:
            print("Nickname not found. Skipping mail.")
            return False

        nickname = row[0]
        print(f"Sending {net_silver_to_send} silver to '{nickname}'...")
        result = subprocess.call(["python", "Send_Mail.py", nickname, str(net_silver_to_send)])
        if result != 0:
            print(f"Send_Mail.py failed with code {result}")
            return False

        print("Mail sent.")
        return True

    except Exception as ex:
        print("ERROR:", ex)
        if cnxn_trade and not cnxn_trade.autocommit:
            cnxn_trade.rollback()
        return False
    finally:
        if cnxn_trade:
            cnxn_trade.close()
        if cnxn_world:
            cnxn_world.close()

if __name__ == "__main__":
    users = get_users_with_market_items()
    print(f"Found {len(users)} users with items listed.\n")

    for user_no in users:
        purchase_and_mail_items(user_no)
 
got this error, can you help
I simply change the ID for admin to 9000 to be the same as the populate market other than changing the database name.

Code:
ERROR: ('07002', '[07002] [Microsoft][ODBC Driver 17 for SQL Server]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')
 
I've read your code from the black market for Send_Mail.py and it did not show exactly the program in python(or i simply did not understand it). So I proceed to create my own python to called for dbo.sendMail and for me, this work.


Python:
# Send_Mail.py
import sys
import pyodbc

# --- Configuration ---
SERVER = '<database name>' # --- please use double \\ if you did not use localhost example, SQL\\Batman
DATABASE = 'SA_BETA_GAMEDB_0002'
DRIVER = 'ODBC Driver 17 for SQL Server'

# Item key for silver (adjust if different)
SILVER_ITEM_KEY = 1

def send_mail_via_stored_procedure(receiver_name, silver_amount):
    conn_str = f'DRIVER={{{DRIVER}}};SERVER={SERVER};DATABASE={DATABASE};Trusted_Connection=yes'

    try:
        with pyodbc.connect(conn_str) as conn:
            cursor = conn.cursor()

            # Prepare parameters
            toFamilyName = receiver_name
            itemKey = SILVER_ITEM_KEY
            itemCount = silver_amount
            enchant = 0
            title = "Market Sale Proceeds"
            contents = f"Thank you for using the World Market!\nNet proceeds (after 15% tax): {silver_amount} silver."

            # --- ✅ FIXED SQL: Declare @RC and @symNo ---
            sql = """
                DECLARE @RC INT, @symNo NVARCHAR(50);
                EXEC @RC = [dbo].[sendMail]
                    @toFamilyName = ?,
                    @itemKey = ?,
                    @itemCount = ?,
                    @enchant = ?,
                    @title = ?,
                    @contents = ?,
                    @symNo = @symNo OUTPUT;
                SELECT @RC AS ReturnCode, @symNo AS ConfirmationID;
            """

            cursor.execute(sql, toFamilyName, itemKey, itemCount, enchant, title, contents)
            result = cursor.fetchone()

            if result:
                return_code = result[0]  # @RC
                symNo = result[1]        # @symNo

                if return_code == 0:
                    print(f"✅ Mail sent to '{receiver_name}'. Confirmation ID: {symNo}")
                else:
                    print(f"❌ Stored procedure returned error code: {return_code}")
                    return 1
            else:
                print("❌ No result returned from stored procedure.")
                return 1

            return 0

    except Exception as ex:
        print(f"❌ Failed to send mail to '{receiver_name}': {ex}")
        return 1

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("❌ Usage: python Send_Mail.py <nickname> <amount>")
        sys.exit(1)

    nickname = sys.argv[1]
    try:
        amount = int(sys.argv[2])
    except ValueError:
        print("❌ Amount must be an integer.")
        sys.exit(1)

    exit_code = send_mail_via_stored_procedure(nickname, amount)
    sys.exit(exit_code)

On other note, do you know why in game UI is not updated automatically that the item is sold? the item will updated if add another item to sell and it will shows the previous sold item to be gone. can't the script notify the game automatically to update the UI?
 
Last edited:
Back