Twitter API bot for generating image out of tweets using tweepy and Craiyon.io

Find the code: here.

After seeing a variety of automated accounts on Twitter recently, I decided to apply for access to the Twitter API for developers. I decided to code it in Python as Twitter has an excellent library called Tweepy which can perform many Twitter-based tasks such as retrieving Tweets or data about specific users.

Now the problem of deciding what would be useful or fun to code. Having seen a lot of tweets about the Dall-E or ‘Craiyon AI’, a picture-generating AI, I decided to make a bot that would return the images generated by the text of the tweet that the account had been tagged in,

Here I will post the process of making a bot along with certain problems I had to overcome in the hope it is useful to other programmers making bots! I am a self-taught programmer so if you have any advice on how I could improve this then please email domgdrigby@dgdrigby.

If you are only interested in the code for the final bot it is in this repository.

Use Notes

Apply for twitter API here

Use the file pickleMaker.py and input your own Twitter API keys. Fill in these details and then pickle the file. I chose to pickle the details rather than just have them stored in text on my computer.

logIn = {"api_key":"", "api_secret_key":"",
"access_token":"", "access_secret":""}

Once you have inputted the keys the code should run.

Code Run Through

  1. Logging in. Tweepy handles logging in with ease. It returns an account object which allows you to run a variety Twitter functions from that account.
def logIn():

    with open("signInDetails.pkl","rb") as file:
        keys = pickle.load(file)
        print(keys["api_key"])

    # Create a file of the sign in details, and then add it to the .gitignore file so that your credentials will not be pushed to GitHub

    api_key = keys["api_key"]
    api_secrets = keys["api_secret_key"]
    access_token = keys["access_token"]
    access_secret = keys["access_secret"]
    
    # Authenticate to Twitter
    auth = tweepy.OAuthHandler(api_key,api_secrets)
    auth.set_access_token(access_token,access_secret)
    
    api = tweepy.API(auth)
    
    try:
        api.verify_credentials()
        print('Successful Authentication')
    except Exception as e:
        print(e)
        print('Failed authentication')

    return api

2. Retrieving mentioned tweets and identifying new ones.

def retrieveNewMentionedTweet(self):
    self.mentions = self.account.mentions_timeline()
    self.taggedTweets = []

    with open(self.storageName, 'rb') as pickle_file: # pickle file of all previously mentioned tweet IDs
        alreadyDone = pickle.load(pickle_file)

    for status in self.mentions:
        try:
            tweet = self.account.get_status(status._json['in_reply_to_status_id']) # object for tweet being replied to
            tweet_ID = status._json['id'] # id off tweet with tag
            if tweet_ID not in alreadyDone: # check we havent already replied to this tweet
                self.taggedTweets.append([tweet,status._json["id"]]) # return an array of the tweet object to make an image, along with the id to reply to
                alreadyDone.append(tweet_ID)
        except :
            pass

    with open(self.storageName,"wb") as f:
        pickle.dump(alreadyDone,f)
        print(alreadyDone)

    return self.taggedTweets

As you can see, the twitter API can supply a list of all the tweets that you have been mentioned in, this function checks if you have already replied to it using a pickle file.

3. Using Selenium Webdriver to get AI generated image from Craiyon.

There probably is a more efficient way to do this, and if you know one please let me know, but I decided to use my experience using Selenium webdriver. This has the added benefit of not taking up any disk space on my computer.

The image was made by simply booting the webdriver to Craiyon.io’s website, inputting the text from the Tweet, and then screenshotting the result. In between these main steps, the program has to wait for the privacy message and click on it.

Loading the browser and then creating the image:

def createImage(self):

        self.driver = webdriver.Firefox(executable_path="./selenium_firefox/drivers/geckodriver")

        self.driver.get("https://www.craiyon.com/")

        self.driver.maximize_window()

        self.driver.execute_script("window.scrollTo(0, 350)") 


        ## must wait for privacy warning

        time.sleep(4)

        try:
            print("Waiting for privacy warning")
            element = WebDriverWait(self.driver, 15).until(
            EC.presence_of_element_located((By.XPATH, "(//button[@class=' css-b2i6wm'])[2]")))
            self.driver.find_element(By.XPATH,"(//button[@class=' css-b2i6wm'])[2]").click()
        
        except:
            print("no privacy warning")

        print(self.driver.title)

        self.text_enter = self.driver.find_element(By.ID,"prompt")

        print(self.text)

        self.text_enter.send_keys(self.text)

        self.text_enter.send_keys(Keys.RETURN)

Waiting for the image to generate:

def waitForImage(self):
    try:
        element = WebDriverWait(self.driver, 150).until(
        EC.presence_of_element_located((By.XPATH,"(//img[contains(@class,'h-full w-full')])[1]")))
        print("Image detected")

    except Exception as e:
        print("Failed to produce image")
        print(e)
        self.driver.quit()

    self.tag = self.text.replace(" ","_")
    self.tag = "images_generated/"+self.tag+".png"
    self.driver.save_screenshot(self.tag)

As you can see the program will then save the image to the image_generated directory. After it is tweeted it is deleted to save memory, using the function below:

def deleteImage(self):
    file_path = self.tag
    if os.path.isfile(file_path):
        os.remove(file_path)
        print("File has been deleted")
    else:
        print("File does not exist")

4. Replying to the tweeter with the image.

In order to reply to the tweet, the tweet ID of the original tweet is needed. A Tweet ID is a unique integer number that Twitter assigns to all tweets when they are posted. It is the way that Tweepy will identify individual tweets, therefore when we are replying we need the ID of the original tweet. When running the GeneralTwitter.retrieveNewMentionedTweet, it returns a list of tweets and their IDs. Therefore when replying to a tweet we just give the sendReplyTweetwithImage() the image file location (tag), the tweet text, and the tweet ID to reply to (element[1]). The bot will then reply with the generated image.

def main(account):
    twitter = GeneralTwitter(account)
    mentionsTweets = twitter.retrieveNewMentionedTweet()
    if mentionsTweets: # empty sequences are False (another way of saying length greater than 0)
        for element in mentionsTweets:
            tweet = element[0] # this is a tweet object
            image = ImageMaker(tweet._json['text'])
            newImage = EditImage(image.tag)
            newImage.crop()
            twitter.sendReplyTweetwithImage(image.tag,
image.text,element[1])
            image.deleteImage()
    else:
        print("No new tweets")
        pass
def sendReplyTweetwithImage(self,imageLocation,status,id):

    print(imageLocation)

    ret = self.account.media_upload(imageLocation)

    # Attach returned media id to a tweet
    self.account.update_status(media_ids=[ret.media_id_string], status=status, in_reply_to_status_id = id , auto_populate_reply_metadata=True)

Leave a Reply

Your email address will not be published. Required fields are marked *