Discord bot with Psycopg I've made a Discord Bot in Python that uses the Psycopg module to make calls to a PostgreSQL database in order to display information.
https://github.com/Chombler/cardbot
Specifically, I call this function when a user calls the bot:
def pullCardRecord(recordName):
    success = True
    try:
        print(&quot;Trying&quot;)
        connection = psycopg2.connect(user = db_credentials[0],
                                        password = db_credentials[1],
                                        host = db_credentials[2],
                                        port = db_credentials[3],
                                        database = db_credentials[4])
        print(&quot;connected&quot;)
        cursor = connection.cursor()

        join_table_query = '''
        SELECT id
        FROM card
        WHERE card.name = ('%s') ''' % (recordName)

        cursor.execute(join_table_query)
        results = cursor.fetchall()
        print(results)
        if len(results) &lt; 1:
            success = False
            raise ValueError('The name that was given to cardbot didn\'t exist in the card table.')

        join_table_query = '''
        SELECT  name, 
                cardclass.cardclass,
                tribe.tribe, cardtype.cardtype,
                cost, side.side, strength, trait.strengthmodifier, health, trait.healthmodifier,
                trait.trait,
                ability,
                flavor,
                cardset.cardset,
                rarity.rarity
        FROM card
        LEFT JOIN cardtoclass ON card.id = cardtoclass.cardid
        LEFT JOIN cardclass ON cardtoclass.classid = cardclass.id
        LEFT JOIN cardtotrait ON cardtotrait.cardid = card.id
        LEFT JOIN trait ON cardtotrait.traitid = trait.id
        LEFT JOIN cardtotribe ON card.id = cardtotribe.cardid
        LEFT JOIN tribe ON cardtotribe.tribeid = tribe.id
        LEFT JOIN cardtype ON cardtype.id = card.typeid
        LEFT JOIN cardset ON cardset.id = card.setid
        LEFT JOIN rarity ON card.rarityid = rarity.id
        LEFT JOIN side ON card.sideid = side.id
        WHERE card.name = ('%s') ''' % (recordName)

        cursor.execute(join_table_query)
        results = cursor.fetchall()


        print(&quot;Printing Results&quot;)
        for row in results:
            for col in row:
                print(col)
            print()

        cardInstance = cardObject(results)
        print(cardInstance.information())

        # Print PostgreSQL version
        cursor.execute(&quot;SELECT version();&quot;)
        record = cursor.fetchone()
        print(&quot;You are connected to - &quot;, record,&quot;\n&quot;)

    except (Exception, psycopg2.Error) as error :
        print (&quot;Error retrieving card information using PostgreSQL,&quot;, error)
    finally:
        #closing database connection.
        if(connection):
            cursor.close()
            connection.close()
            print(&quot;PostgreSQL connection is closed&quot;)
        return(cardInstance.information() if success else &quot;I'm sorry, I couldn't find a card with that name.&quot;)

I want to make sure that the bot is protected from any direct sql injections before I deploy it. Is it currently fairly safe or do I need to add anything to protect its integrity?
Unpacking
Try this rather than hard-coded indexing:
    user, password, host, port, database = db_credentials
    connection = psycopg2.connect(user, ...)

Or, if you're able to rearrange those parameters such that they match those of the connect signature:


dbname – the database name (database is a deprecated alias)
user – user name used to authenticate
password – password used to authenticate
host – database host address (defaults to UNIX socket if not provided)
port – connection port number (defaults to 5432 if not provided)


then you can simply do
connection = psycopg2.connect(*db_credentials)

Logs
Consider replacing
    print(&quot;Trying&quot;)
    print(&quot;connected&quot;)

with calls to the actual logging module, which is more configurable and maintainable.
Quote escapes
'The name that was given to cardbot didn\'t exist in the card table.'

can be
&quot;The name that was given to cardbot didn't exist in the card table.&quot;

In-application queries
Your join_table_query is long. There are several approaches to improve this - either save a view in the database (my favourite), or a stored procedure (common but I think it's overkill in this case).
Injection

the bot is protected from any direct sql injections before I deploy it

This is directly vulnerable:
'''... WHERE card.name = ('%s') ''' % (recordName)

Never (ever) use string formatting to insert parameters to a query. All DB connection libraries have anticipated this concern and most approach it using &quot;prepared statements&quot;.
This article is old but relevant. The actual reference shows that you should be passing vars as a sequence or a mapping, which will prevent injection.