Twitter....I Mean X Bot Roadblock

Twitter....I Mean X Bot Roadblock

My ongoing attempt at creating a bot for my partner's plant shop and progressing with Day 24 of 100-Days-Of-Python.


6 min read

Getting this started

After the Email Sender project, I scouted again to see what would be an excellent project but a bit more challenging and more practical for day-to-day use (I don't plan to actually use the auto email least not yet ๐Ÿ˜). So after discussing things I could do that would benefit both of us, we figured that a Twitter bot would help us both. She'd have a tool to automate her Twitter posts of her plants and I'd get some practice building out Python bots. It seemed easy enough to get all of the details:
- Set up a Twitter Developer Account
- Code the Bot
- Spice up the bot
- Deploy the Bot

Tweepy is the popular library of choice for working with Twitter's API and their documentation is understandable. For the sake of clarity, I'm going to reference X as Twitter (like the good lord intended ๐Ÿ˜‚)

API Permissions are the battle

The main issue I'm having with this bot creation is with API Permissions. Tweepy's docs help a good amount but I'm not sure what I'm missing between their doc ( and Twitter's ( My permissions for the Twitter account are for API V2 and restricted V1 access. However, no matter what I did to change the code to get a post to work, I kept getting an error about API permissions.

SpongeBob gif. While it rains outside, SpongeBob sits alone at a booth in the Krusty Krab, staring blankly at a steaming mug.

I tried to see if I could access the few API v1 endpoints I had access to but that didn't work either. My working repo is here -> and you can see how I currently have it set up (if anyone knows what I'm doing wrong let me know ๐Ÿ™๐Ÿพ). Most attempts were to the POST endpoint but I tried to GET a list of recent tweets as well as read my follower count but no luck there either. The closest I got was where I had a setup that prompted me to navigate to the URL provided to get a code to authenticate, but once I go to that URL (Which has my ID, redirect URL, and Username data in it) I get an error.

After coming back to this a handful of times with a "fresh mind" but no progress with this issue, I've decided to pause it and see if any of my peers might know what I'm missing, instead of frustrating myself day in and day out. Considering that my lady still needs to create the content that I'd be posting (listing images, deals, etc.) I can take a breather and work on something else while I try to source some guidance. That led me to move on to Day 24 in my 100-Days-Of-Python course (yes....I'm still planning on finishing all 100 days ๐Ÿ˜„) as well as pick a new project that is more useful for now while I figure out the Twitter bot and wait for content to post. The new project is a "Personal Finance Tracker with Predictive Analysis". The predictive analysis portion of the project is extra credit since it will involve ML which I have never dove into. This might also sound more difficult than the last project, and I'm sure it is ๐Ÿ˜…, but it's something fresh that I need.

Day 24 - Files, Directories, and Paths

This day came pretty easy. It's pretty much just an overview of directories and how to access the files within through relative and absolute paths. Reading and Writing files are easy peasy as well utilizing one main method, open(). open() returns a file object, and is most commonly used with two positional arguments and one keyword argument: open(filename, mode, encoding=None). The first argument is a string that will be the file name or the path to the file you're trying to read. The mode argument is to determine which way the file is to be used. You can read mode="r", write mode="w", and append mode="a" for most people's needs. There are a couple more, like r+ for reading and writing and b for opening the file in binary mode.

A quick example of this would be:

open('letter.txt', 'r')
open('letter.txt', mode='r')

Personally, I prefer open('letter.txt', mode='r') so whoever reads the code, is provided more clarity on what that is doing, but to each their own. This leads us to a nice lil' trick for Python to ensure the file closes after the update is made. We do this by using with and as like so:

# Example 1
with open('letter.txt', mode='w') as new_letter:
    """The text below will update whatever text is on letter.txt"""
    new_letter.write("New letter, who dis?")

# Example 2
with open('letter.txt', mode='r') as new_letter:
    """The call below will read letter.txt and store the data in `contents`."""
    contents =

# Example 3
with open('letter.txt', mode='a') as new_letter:
    """The call below will append the added text to letter.txt at the end of the file."""
    new_letter.write("It's ya boi, that's who?")

There's more to learn about "Input and Output" though and I recommend taking a peek at the docs ->

The Project - Mail Merger Project

The project for day 24 was not as hefty as some of the more recent days, which I appreciate very much ( อกโฟโ€ฏอœส– อกโฟ). The goal was to take a list of names from a given doc and create email drafts for each person. The content was the same but the names needed to be swapped out for each new file and labeled as such in the file name and body copy. Outside of using a for loop, this project consisted of only what we learned on day 24 and the code is brief:

PLACEHOLDER = "[name]"

with open("/Users/kyleleonard/100-Days-Of-Code_Python/day-24/Input/Names/invited_names.txt") as names_file:
   names = names_file.readlines()

with open("/Users/kyleleonard/100-Days-Of-Code_Python/day-24/Input/Letters/starting_letter.txt") as letter_file:
    letter_content =
    for name in names:
        stripped_name = name.strip()
        new_letter = letter_content.replace(PLACEHOLDER, stripped_name)
        with open(f"/Users/kyleleonard/100-Days-Of-Code_Python/day-24/Output/ReadyToSend/letter_for_{stripped_name}", mode="w") as completed_letter:

That's it! Now the files created from running this are listed in the repo and you can check it all out here ->


My main goal now is to get started on that other project I mentioned while also tackling days 25-30. I'm starting to wind down with all the weddings vacations, birthdays, and everything in between, and am VERY happy about it. I never realized how exhausting travel could be when you're doing it in succession for a few months. Nonetheless, there are a couple of new things out in the dev world that have been released over the last month:

These 2 intrigue me the most and I plan to use Bun as my runtime for my new project. I know this will inevitably cause me a headache with the impending wall of errors I plan to face. It should be good for me though, forcing me to get acquainted with Bun and maybe even trying to contribute to it or other open-source tools I use. Fingers crossed ๐Ÿคž๐Ÿพ and until next time, โœŒ๐Ÿพ.

Did you find this article valuable?

Support Kyle Leonard by becoming a sponsor. Any amount is appreciated!