7.5.3. Demo: Trolling a Reply Bot#

Choose Social Media Platform: Reddit | Discord | Bluesky | No Coding

We are later going to build a bot that, if you post at it:

  • “Hi @mybotname.bsky.social, please ___” (where the ___ is some action)

  • then the bot will reply, “I will now ____” (where the ___ is that same action).

Then we will try trolling it, and fixing it, and trolling it again.

First though we need to do our bluesky atproto setup:

Log into Bluesky (atproto)#

These are our normal steps get atproto loaded and logged into Bluesky

from atproto import Client

(optional) make a fake Bluesky connection with the fake_atproto library For testing purposes, we”ve added this line of code, which loads a fake version of atproto, so it wont actually connect to Bluesky. If you want to try to actually connect to Bluesky, don’t run this line of code.

%run ../../fake_apis/fake_atproto.ipynb
Fake atproto (bsky.app) is replacing the atproto.blue library. Fake atproto doesn't need real passwords, and prevents you from accessing real Bluesky
# Login to Bluesky
# TODO: put your account name and password below

client = Client(base_url="https://bsky.social")
client.login("your_account_name.bsky.social", "m#5@_fake_bsky_password_$%Ds")
Fake atproto is pretending to set up a client connection to: https://bsky.social
Fake atproto is pretending log into your account: your_account_name.bsky.social

Bot 1: do whatever we are told#

Our first bot will find our latest mention, and then reply with whatever it is told to do.

First we need to look up our Bluesky handle

# look up my Bluesky handle
my_handle = client.me.handle

Now we will create a string with the pattern we are looking for, but with our actual bot name

expected_pattern = "Hi @" + my_handle + ", please "
print(expected_pattern)
Hi @mybotname.bsky.social, please 

find my latest mention#

We need to find the latest bluesky post that mentions us and match our expected pattern.

We do this by first finding our user handle, then requesting

We do this by looking in our reddit inbox for messages (we limit it to one, since we just want the latest).

It doesn’t directly give us the one message (instead it is in something called an “iterator”), but we can use the next function to get the message out.

We then display the subject of the message just so we can see that it found something..

# search posts that mention my handle
mentions = client.app.bsky.feed.search_posts({
    'q': expected_pattern, 
    'mentions': my_handle
}).posts

if(len(mentions) < 1):
    print("Error: Could not find matching mentions!")
    
latest_mention = mentions[0]

If post matches our pattern, reply#

Now, if the mention text starts with that expected pattern, then we will find the action from the end of the mention text (based on the expected_pattern length), and reply using that action:

We also add “else” cases for when we didn’t match the patter, and display a message saying what didn’t match.

# check if the mention text starts with our set phrase
if latest_mention.record.text.startswith(expected_pattern):
    
    # get the action, which should be the end of the string after the expected pattern
    action = latest_mention.record.text[len(expected_pattern):]

    # make a new message which says we will do the action
    message = "I will now " + action

    # send our message in reply
    client.send_post(
        message, 
        reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                  'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
    )
    
else: # else code for if the message subject didn't match
    display("The message (" + latest_mention.record.text + ") didn't match our expected pattern (" + expected_pattern + ")" )
Fake atproto is pretending to post:
I will now jump

Yay! It worked!

But there is a problem with our bot!

Trolling bot 1#

This bot is really easy to troll, so if I repeat my steps and get a new mention (this code is just a duplication of the code above):

# search posts that mention my handle
mentions = client.app.bsky.feed.search_posts({
    'q': expected_pattern, 
    'mentions': my_handle
}).posts

if(len(mentions) < 1):
    print("Error: Could not find matching mentions!")
    
latest_mention = mentions[0]


# check if the mention text starts with our set phrase
if latest_mention.record.text.startswith(expected_pattern):
    
    # get the action, which should be the end of the string after the expected pattern
    action = latest_mention.record.text[len(expected_pattern):]

    # make a new message which says we will do the action
    message = "I will now " + action

    # send our message in reply
    client.send_post(
        message, 
        reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                  'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
    )
    
else: # else code for if the message subject didn't match
    display("The message (" + latest_mention.record.text + ") didn't match our expected pattern (" + expected_pattern + ")" )
Fake atproto is pretending to post:
I will now do something horrible!

Someone messaged us saying: I want you to do something horrible!, and we replied I will now do something horrible!.

They could have made us post much worse!

Bot 2: Trying to limit actions#

Let’s try this again, but limit the actions we will do.

  • If someone asks us to “run”, “jump”, or “fly”, we will do it

  • If someone asks us to do something else we will say:

    • “I do not recognize the command ___” (with __ being whatever they said)

So, to go back through our steps:

find my latest mention#

# search posts that mention my handle
mentions = client.app.bsky.feed.search_posts({
    'q': expected_pattern, 
    'mentions': my_handle
}).posts

if(len(mentions) < 1):
    print("Error: Could not find matching mentions!")
    
latest_mention = mentions[0] 

# display the subject and body of the message, so we can see what we found
display("latest post text: " + str(latest_mention.record.text))
'latest post text: Hi @mybotname.bsky.social, please fly'

If message matches our pattern, reply#

We do the same code for this as before, but after we get the action we are told to do, we put another if/else to either do the action if we recognize it, or say we didn’t recognize the action.

We will use in to see if the action is in our list of allowed actions (called an allow_list)

actions_allow_list = ["run", "jump", "fly"]


# check if the mention text starts with our set phrase
if latest_mention.record.text.startswith(expected_pattern):
    
    # get the action, which should be the end of the string after the expected pattern
    action = latest_mention.record.text[len(expected_pattern):]

    # See if it is one of our recognized actions
    if(action in actions_allow_list):
        # make a new message which says we will do the action
        message = "I will now " + action

        # send our message in reply
        client.send_post(
            message, 
            reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                      'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
        )
        
    else: # we didn't recognize the action
        # make a new message which says we will NOT do the action
        message = "I do not recognize the command " + action

        # send our message in reply
        client.send_post(
            message, 
            reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                      'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
        )

else: # else code for if the message subject didn't match
    display("The message (" + latest_mention.record.text + ") didn't match our expected pattern (" + expected_pattern + ")" )
Fake atproto is pretending to post:
I will now fly

That one was in our allow list so it worked. Let’s do it all again, with the tweet that caused us problems last time

Note: the code below is just copied from the code sections above

# search posts that mention my handle
mentions = client.app.bsky.feed.search_posts({
    'q': expected_pattern, 
    'mentions': my_handle
}).posts

if(len(mentions) < 1):
    print("Error: Could not find matching mentions!")
    
latest_mention = mentions[0] 

# display the subject and body of the message, so we can see what we found
display("latest post text: " + str(latest_mention.record.text))


actions_allow_list = ["run", "jump", "fly"]


# check if the mention text starts with our set phrase
if latest_mention.record.text.startswith(expected_pattern):
    
    # get the action, which should be the end of the string after the expected pattern
    action = latest_mention.record.text[len(expected_pattern):]

    # See if it is one of our recognized actions
    if(action in actions_allow_list):
        # make a new message which says we will do the action
        message = "I will now " + action

        # send our message in reply
        client.send_post(
            message, 
            reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                      'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
        )
        
    else: # we didn't recognize the action
        # make a new message which says we will NOT do the action
        message = "I do not recognize the command " + action

        # send our message in reply
        client.send_post(
            message, 
            reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                      'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
        )

else: # else code for if the message subject didn't match
    display("The message (" + latest_mention.record.text + ") didn't match our expected pattern (" + expected_pattern + ")" )
'latest post text: Hi @mybotname.bsky.social, please do something horrible!'
Fake atproto is pretending to post:
I do not recognize the command do something horrible!

Ok, this time we said I do not recognize the command do something horrible!.

That looks a little better! Are we safe now?

Trolling bot 2#

No, it turns out we are not safe.

Let’s find the latest mention again and see what happens

# search posts that mention my handle
mentions = client.app.bsky.feed.search_posts({
    'q': expected_pattern, 
    'mentions': my_handle
}).posts

if(len(mentions) < 1):
    print("Error: Could not find matching mentions!")
    
latest_mention = mentions[0] 

# display the subject and body of the message, so we can see what we found
display("latest post text: " + str(latest_mention.record.text))


actions_allow_list = ["run", "jump", "fly"]


# check if the mention text starts with our set phrase
if latest_mention.record.text.startswith(expected_pattern):
    
    # get the action, which should be the end of the string after the expected pattern
    action = latest_mention.record.text[len(expected_pattern):]

    # See if it is one of our recognized actions
    if(action in actions_allow_list):
        # make a new message which says we will do the action
        message = "I will now " + action

        # send our message in reply
        client.send_post(
            message, 
            reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                      'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
        )
        
    else: # we didn't recognize the action
        # make a new message which says we will NOT do the action
        message = "I do not recognize the command " + action

        # send our message in reply
        client.send_post(
            message, 
            reply_to={'root': {'uri': latest_mention.uri, 'cid': latest_mention.cid}, 
                      'parent': {'uri': latest_mention.uri, 'cid': latest_mention.cid}}
        )

else: # else code for if the message subject didn't match
    display("The message (" + latest_mention.record.text + ") didn't match our expected pattern (" + expected_pattern + ")" )
"latest post text: Hi @mybotname.bsky.social, please stop talking. But that doesn't mean I won't say horrible things like: I hate everybody!"
Fake atproto is pretending to post:
I do not recognize the command stop talking. But that doesn't mean I won't say horrible things like: I hate everybody!

Oh no! Someone messaged at us:

  • Hi @mybotname.bsky.social, please jump stop talking. But that doesn't mean I won't say horrible things like: I hate everybody!

And we replied:

  • I do not recognize the command stop talking. But that doesn't mean I won't say horrible things like: I hate everybody!

Making a bot that is troll proof is very difficult! You either need to severely limit how your bot engages with people, or do a ton of work trying to prevent trolling and fix problems when people find a new way of trolling you.

If you want to learn more, you can revisit the story of what went wrong with the Microsoft Tay bot: How to Make a Bot That Isn’t Racist