. tgeorge.net
11/05/2018 at 02:11 PM

Well, it's been over a year since I've blogged, so I guess it's probably a good time to get back at it. I haven't done a blog post about writing code yet, but this project seems like a good one to document as I go along. So let's get started!

I sell on eBay. I don't sell a lot. In fact, I usually only sell 1 thing. But, I'm the only person on the internet who sells this particular item, so it's a pretty decent side hustle! The only issue is... well, I'm lazy. So sometimes I don't always ship it out in a timely manner. To help this, I started prepackaging them. I print out a batch of return labels, and a batch of stamps, throw some in envelopes, and wait. Then when someone buys them, I just print the label with their address, throw it on a ready to go envelope, and drop it in a box down the street. But.... sometimes I'm still too lazy for that. Especially when I have a few orders queued up. All the copying and pasting from eBay, going back and marking the right item as shipped, ugh, it's such a hassle. (Okay, maybe I'm being over-dramatic, but still.) Regardless, I figured there must be a better way!

(Side note: I used to sell on Amazon as well, but my laziness got me blocked :( I've protested several times and each time my request to allow me to sell again gets denied. Hopefully someday I can sell on Amazon again, and I'll update my new software to use Amazon too.)

Luckily... I happen to know a (I like to think REALLY GOOD) software developer. (Hint: it's me!) So, let's write a script to automate a little bit more of this!

I can break my desired software into 3 simple steps:

  1. Get all non-shipped orders from eBay.
  2. Print out shipping labels for each order.
  3. Tell eBay to mark the order as shipped.

So, let's get started on step 1!

eBay offers a really solid API. An API is an Application Programming Interface. Basically, eBay has written code that is hosted on their server. I can call that code from my own software. eBay also offers a wrapper for it's code in C# using NuGet, so that's what I'll be using. An API wrapper means they've already written all the code to call the code, so I call the code that calls the code. Okay, maybe that sounds complicated, but trust me, it's easy! 

The first thing I have to do is authenticate. That means I have to tell eBay's API who I am. Luckily, I'm writing this as a console application for just me, which makes it easier. If I was writing some complicated software that allowed any eBay user to use my code, it'd be way more complicated. I already had an eBay developer account, so I logged into that, created a key set, then I was able to create a UserToken. This UserToken allows me to call eBay's API as my user. This does expire sometime next year, so I'm going to add it to my app.config as an app setting, so I can change it on the fly as needed. It's key is "userToken". Now when I need it in my code I just use:

var userToken = ConfigurationManager.AppSettings["userToken"];

Now, I need to create an ApiContext. This is the object that I will use when calling eBay's API, which contains my userToken. Luckily, again, using eBay's API wrapper, it's super simple:

using eBay.Service.Core.Sdk;
var apiContext = new ApiContext { ApiCredential = new ApiCredential(userToken) };

Now I can use the apiContext to get my orders! eBay doesn't have a way to only get unshipped orders, so I have to get all my orders, then see if it's shipped or not. To do this, I'm creating a new method called, well, GetOrders. It accepts my apiContext as an argument.

static OrderTypeCollection GetOrders(ApiContext apiContext)
var getOrdersCall = new eBay.Service.Call.GetOrdersCall(apiContext)
NumberOfDays = 14
return getOrdersCall.ApiResponse.OrderArray;

Now I call my new GetOrders method like this:

var orders = GetOrders(apiContext);

Then I can simply loop through the orders to see which are unshipped.

foreach (OrderType order in orders)
if (!order.ShippedTimeSpecified)
// This is where I'll do stuff!

Now, I need their shipping address. That's included as an AddressType in the OrderType object. I created a quick (and ugly) method to format that the way my printer needs it.

static string FormatAddress(AddressType addressType)
var address = new StringBuilder();        address.Append(addressType.Name); address.Append("\r\n"); address.Append(addressType.Street1); address.Append("\r\n");
if (!string.IsNullOrEmpty(addressType.Street2))
{                address.Append(addressType.Street2); address.Append("\r\n"); } address.Append($"{addressType.CityName}, {addressType.StateOrProvince} {addressType.PostalCode}"); return address.ToString(); }

Which I can call like this:

var address = FormatAddress(order.ShippingAddress);                                               

A lot of people used to buy 2 or 3 of my item, so I started selling it in 2 and 3 packs as well. So I technically have 3 item's for sale on eBay, and people can buy multiple quantities of each. Luckily, I require immediate payment, so each order only has 1 transaction, with a quantity. So, I took each item, got it's item ID, then added those as app settings, with the value being how many are included in that listing. Using this method also allows me to continue to sell other random stuff on eBay, and not have it interfere with this.

<add key="itemIdFor1Pack" value="1" />
<add key="itemIdFor2Pack" value="2" />
<add key="itemIdFor3Pack" value="3" />               

Now I need to get the transaction, then I can get the item from that, get the itemID, look it up in my appsettings to determine the initial quantity, and multiply that by the quantity purchased. So, if someone bought 2 of my 3 packs, my total quantity would be 6. I'll explain why I need the quantity in a little bit.

var transaction = order.TransactionArray[0];
var item = transaction.Item;
var itemFromConfig = ConfigurationManager.AppSettings[item.ItemID];
if (itemFromConfig == null) continue; varinitialQuantity = int.Parse(itemFromConfig);
var quantity = initialQuantity * transaction.QuantityPurchased;  

Now, onto step 2.                                              

For my stamps and labels, I use a DYMO LabelWriter 450 TwinTurbo. This printer allows me to print shipping labels and allows me to buy and print stamps using Encidia. It's super convenient. Dymo also offers an SDK (Software Development Kit) which makes printing to it from my own custom software super simple! In fact, it's only 6 lines of code to print a shipping label. I followed this tutorial here to print from C#. That tutorial offers a lot of insight into how to do it, but I was able to remove a lot of that code as it wasn't needed. But basically, I created a label template, which has a spot for the address, and a spot for the quantity. I put the quantity here, so I know which envelope to put the label on, since they're all prepackaged with 1, 2, or 3 written on them. It just makes it easier.

static voidPrintShippingLabel (string address, int quantity)
var label = Label.Open(@"C:\Users\Timothy\Documents\DYMO Label\Labels\address-bottom-template.label");
label.SetObjectText("Address", address);
label.SetObjectText("Quantity", quantity.ToString());
var printParams = new LabelWriterPrintParams();
printParams.RollSelection = RollSelection.Left;
            label.Print("DYMO LabelWriter 450 Twin Turbo", printParams);

Whew, step 2 was way easier than step 1. Let's move onto step 3! 

This ended up being super easy, too, after everything I've already learned about eBay's API in step 1. I just needed to create a new function, called ShipOrder, which accepted the apiContext, transactionId, and itemId as arguments. The really cool part, though, is that I can also leave buyer feedback automatically right here too!

static void ShipOrder(ApiContext apiContext, string transactionId, string itemId)
var fulfillOrderCall = new CompleteSaleCall(apiContext)
TransactionID = transactionId,
FeedbackInfo = new FeedbackInfoType { CommentType = CommentTypeCodeType.Positive, CommentText = "Fast Payment! A+ Buyer!!" },
Shipped = true,
ItemID = itemId


All done! That ended up being pretty simple when it was all said and done! I went back and added some try/catch blocks, though. In theory, it should never error out, but in case it does, it'll fail gracefully. I also added a fun icon to the project and added it to my task-bar. Now I just click it whenever I'm ready, and it'll do all the work!

View the final product here: https://gist.github.com/tgeorge91/02cd1ddc1ff027011d97fc204a8fdcdf 

All in all, this was about 4 hours of work. It was a fun challenge and I'm super excited that it actually worked! Hopefully this helps to streamline my process and deliver a customer experience on my eBay store.

UPDATE 11/17/2018:

I had one cool person who "accidentally" placed an order and requested that I cancel it. I did this all on eBay's website, but when I ran my script, it still printed their label! Luckily, I was sort of expecting this, so it was easy to fix. For some reason, the order status from eBay's API was still "Active" even though "Cancelled" is an option. I discovered that the best way to do resolve this (best way - okay maybe easiest way?) was to check that the AmountPaid was greater than 0, since the order I refunded had a negative value. I updated the following line to this:

if (!order.ShippedTimeSpecified && order.AmountPaid.Value > 0)

And everything is good to go again! I've updated my gist to reflect this as well.

01/01/2017 at 10:04 PM
Hello and Happy 2017! 2016 was an incredible year and I'm extremely excited for what 2017 has to offer! So let's start with the basics.

Where I'm at: my bedroom
What I'm doing: watching Parks and Rec

Okay! Now on to some of my 2016 New Years Resolutions and how I did on those.

  1. Work out 30 minutes everyday
    I failed at this. I maybe worked out 30 minutes total in 2016.
  2. Read 1 chapter a day
    Failed. Although, I did finish an entire book (3 - actually), which is a big step since I hadn't done that since middle school. (Thanks to Google for getting me through high school without reading!)
  3. Start a new hobby
    I started collecting Coca Cola memorabilia. I've always liked Coke stuff, but I never considered collecting it. One day, when I was browsing craigslist, I found someone's entire lifelong collection for sale for a decent price, and I bought it! I filled up my brother's tank (Ford Expedition) with stuff (literally filled it) and took it home! Although, I have since decided, that I need to sell it. So hopefully I can turn a decent profit in 2017!
  4. Eat out less
    Uhhh, failed. No real explanation needed for this. Taco Bell is just too good.
  5. Journal everyday
    I did good on this until mid June, when I got bored and gave up.
  6. Freelance
    I guess I did okay on this! I did a few small jobs. But I definitely want to step it up a notch in 2017!
  7. Go to as many concerts as possible!
    In mid 2014 I moved out of St. Louis to Moline, IL, where I stayed for a year before returning. In that year, I missed a lot of concerts, and promised myself to not miss any in 2016! I missed a few, but attended a lot. Overall, event wise in 2016, I attended 20 Cardinal's baseball games, 2 Cirque Du Soilet shows, 2 Blue's hockey games, 4 stand up comedy events (John Mulaney and Nick Kroll, Bill Burr, Chris D'Elia, and TRACY MORGAN!), 2 concerts at the St. Louis Symphony Orchestra, 2 musicals (The Little Mermaid and The Book of Mormon), and 28 concerts! The concerts, including some openers, included Blackbear, Hoodie Allen, Super Duper Kyle, Never Shout Never, Metro Station, A Great Big World, American Authors, Justin Bieber, The Summer Set, Call Me Karizma, The Former Me (x3), Miranda Lambert, Kip Moore, The Ready Set, Emblem3, Megan Nicole, Keith Urban, Brett Eldridge, Maren Morris, Brantley Gilbert, Justin Moore, Colt Ford, Darius Rucker, Dan and Shay, Michael Ray, Blue October, Selena Gomez, G-Eazy, Logic, Kenny Chesney, Alessia Cara, Coldplay, Jason Aldean, Thomas Rhett, Twenty One Pilots, Nick Jonas, Demi Lovato, Mike Posner, Bowling for Soup, Dierks Bentley, Randy Houser, 5 Seconds of Summer, Luke Bryan, Little Big Town, Dustin Lynch, Toby Keith, Blink 182, Bryan Adams, Good Charlotte, Sum 41, We The Kings, Yellowcard, and 3OH!3. I know I probably missed a few, but you get the gist!
A few other notable 2016 events were meeting my girlfriend Jessica, quitting drinking soda, getting a new job, gaining a brother (in law), visiting Disney Land and Sea World, registering my LLC, voting for the first time, and last but not least, finding out I'm going to be an uncle!

So, now, onto 2017! Here are a few of my 2017 resolutions.

  1. Work out once a week
    Let's start out slow here. Maybe in 2018 I can step that up.
  2. Read a chapter a day
    Let's try this one again!
  3. Buy a house
  4. Freelance
    I really need to step this up in 2017... so if you know anybody that needs a new website or any custom software... send them my way!
  5. Eat out less
    I keep telling myself this one will be easier once #3 is completed. So we'll see.
  6. Start Game of Thrones
    Most people say they want to watch less TV in the new year... not me!
  7. Take shorter showers
    I take too long in the shower. It's just so hot and relaxing, like a sauna. But I'm going to work on that in 2017!
  8. Get my first app published in the App Store
  9. Visit a new baseball stadium
    Besides Busch, I've been to Wrigley (Chicago), Miller (Milwaukee), Great American (Cincinnati), and Coors (Colorado). I want to make that list longer in 2017! (and visit them all before I die!)
  10. Spend 30 minutes programming everyday, outside of work
  11. Keep my room clean
  12. Finish 1 Trello card every week
    I use Trello for all of my personal projects and goals. I currently have over 150 tasks in my to do lists.
  13. Get up earlier
    I sleep in too much. I'm going to work on that in 2017, too!
As you can see, I have some pretty big plans for 2017. Hopefully I can stick with them! 

Until next time,
04/18/2016 at 09:47 PM
Hi everyone! I'm back finally with a 2nd blog post. I was hoping I'd blog more than this, but like I said in my last post, I'd have nothing to blog about since I actually made a blog. But again, let's get started with the basics.

Where I'm at right now: My bedroom
What I'm doing: Listening to The Life of Pablo by Kanye West

A few months ago, a friend of mine had a few people over for drinks. After a few too many, I woke up the next morning with a note in my iPhone reading "Blog about Beliebership." So, here I am. I'm not sure what I wanted myself to actually blog about, but with me attending my 3rd Justin Bieber concert tomorrow, today seemed like a good time to get this knocked out.

So let's go back to where it all began: 2010. It was my senior year of high school and I was 18 years old. I was the typical 18 year old guy, and I hated Justin Bieber, for absolutely no reason. It was just the thing to do. We all hated Bieber. I couldn't even name a song of his, but I knew that I hated him! He was a little younger than us, he was making a lot of money, and all the girls liked him; so we hated him. Until one day it all changed. I wish I knew the exact date, but I don't. I kept hearing this song on the radio that I really liked. It had a really catchy chorus that went "Baby, baby, baby, ohhh" and I was addicted. It wasn't for a few weeks that I decided to find out who sang that song I kept hearing. I remember exactly where I was at that moment. It was in Springfield, IL, and I was on my way home from school, driving East on Sangamon Ave. I stopped at the stop sign at the corner of Sangamon Ave and 8th St (which is now a stop light) and sat there as I shazamed the song on my phone. I was in utter disbelief when I realized this was a Justin Bieber song. I liked a Justin Bieber song. After all these months of trashing Bieber and calling him awful names that my more mature (and politically correct) self wouldn't repeat, I liked a song of his. I just drove home. Confused about life. When I got home I decided to get on YouTube and see if I liked any of his other music... and I did. I tried to figure out what to do about this. I decided to just come out and admit it. I was a Belieber.

I decided to fully embrace my new-found addiction. I told all my friends about it and even bought a Justin Bieber shirt. A friend of mine, named Matt, also bought a matching shirt with me, and we even wore them to school one day, although our school had a "no band shirts" policy (stupid private schools) so Matt got in trouble for his shirt, but luckily I was late to school that day, and Matt informed me, so I wore a hoodie and my shirt went unnoticed. 

Matt and I with our matching Justin Bieber shirts

My Beliebership went on for another 2 years before I decided to see him in concert. So, on October 27, 2012, I saw him in concert for the first time. It was one of the best days of my life. Cody Simpson and Carly Rae Jepsen opened for him. Until this day, I had never heard of Cody Simpson, but I'm a big fan of his now, too. (See my 2 favorite Cody Simpson songs here and here, along with his collab with Justin Bieber, here) Carly Rae Jepsen was the other opener, who was big at the time for her hit song "Call Me Maybe."

Nine months later, my best friend Shayna (who was also at the first show) and I drove to Chicago, to see him a 2nd time. July 9, 2013. Justin kind of sounded like he maybe had a cold, so he wasn't as good as the first time, but was still awesome. However, as much as I liked Cody Simpson and Carly Rae Jepsen, I liked the openers this time even better. The show started off with Mike Posner, who you probably recognized from his 2010 hit "Cooler than me." Mike Posner (along with Blackbear, who is another of my favorite artists) co-wrote "Boyfriend" by Justin Bieber, so this allowed him to land a spot on this leg of Justin's tour. (Mike Posner is another one of my favorite artists - who I met last Summer for the first time!) The 2nd opener was Hot Chelle Rae. Hot Chelle Rae is awesome live and I really want to see them again (listen to my 2 favorite HCR songs here and here.)

Shayna and I after the 1st Justin Bieber concert we attended

Almost 3 years have gone by since then. It's been a rough time for us Beliebers, but the true ones have stuck through it all. (Actually, it's never easy being a straight, male belieber, but it was more difficult during this time.) Between his DUI, him egging his neighbors house, the monkey, a Comedy Central roast, the Jelena breakup, and lots more, it's been a struggle. But watching him come back after all of it has been an extremely rewarding process.

It's been 1,014 days (or 2 years, 9 months, and 9 days) since I've seen him in concert. But tomorrow, that changes. Tomorrow I see Justin for the 3rd time. I'll be with Shayna, again, and this time our other friend, Kayla. It's the moment I've been looking forward to since before he even announced this tour, and it's going to be EPIC!

I'm signing off for now, but I hope you enjoyed reading about my adventure of Beliebership, and I hope this covered everything that not-so-sober Timothy wanted me to cover.

Again, hopefully I'll be back soon!

12/26/2015 at 04:18 PM

Hello there! I have a blog now! I've always wanted to have a blog, in fact I've had several throughout the years, but I'm really bad at updating them. I always have lots of random thoughts and say "hey, I could blog about that, if I had a blog!" so alas, I have a blog. With that being said, I'll likely have nothing to talk about now.

So let's get started with the basics:

Where I'm at right now: My parents house, the house where I spent the first 18 years of my life, in Springfield, IL.
What I'm doing: Watching some dumb movie on Hallmark about a girl who's stalking a guy, but they're going to fall in love. Because that's realistic!

You're probably wondering what I'm going to blog about. I'm wondering that, too, actually. Here's a list.

Things I plan to blog about:

  • Personal stuff, but not too personal
  • Programming/Web Development/Technology
  • Cars, I love my car(s), but I hate them at the same time
  • Other interesting/cool stuff

Stuff I definitely won't blog about:

  • Politics, because politics are stupid. That's about it.

Hopefully I'm back soon!