Automate Everything w/ Bash, Linux & Command Line
  1. Pinterest Pin It Button/Bookmarklet Hybrid

    AKA - Pinterest “Pin It” Button Implementation Revisit

    I wrote a post about a year ago about how to install the Pinterest Pin It button. Why would I have to do that? Well, because the code generated in the developer docs was incomplete, confusing and led to many many broken implementations on sites. I know that’s true because of how popular that post was.

    Read the old post here.

    Pin It - A Year Later

    Well, I’m writing this post because many of the same issues still exist. They have done a better job of making sure that people understand how to install the code, but they’ve still allowed it to be easy for people to install it incorrectly on their sites. The biggest problem is when the media parameter isn’t tagged properly in the button href.

    The original version would just fail if an image wasn’t specified. The new version allows you to pin the page, with a broken image. How much sense does that make?

    The Pin It Bookmarklet Works…

    What makes all this worse is that the Pinterest bookmarklet that they’ve had since the very beginning has more functionality in it than the button does. Ever notice that? It’s really mind-boggling.

    Why wouldn’t they just mimic the functionality of the bookmarklet with the Pin It button? When you use the bookmarklet it gives you a gallery of images for you to choose from to use in your pin. That makes the bookmarklet usable on many different types of pages with no configuration required. Whereas the button has to have one image hard coded to it. This makes zero sense.

    So I thought… Why not just rewrite the button functionality so that it calls the Javascript from the bookmarklet instead of the code the button would normally run? Would that work? YES IT DOES!

    The Pin It Button - Pin It Bookmarklet Hybrid

    The implementation is really simple. Just start out by adding the following code, as recommended by Pinterest, on the page where you want the button to appear. The one change is that I’ve added an ID to a surrounding <span> tag.

    <span id="pin-it"><a href="//pinterest.com/pin/create/button/"><img src="//assets.pinterest.com/images/pidgets/pin_it_button.png" alt="image" /></a></span>
    

    Then, just add the following script to your page. Make sure you have the jQuery library on the page as well.

    <script type="text/javascript">
        jQuery.getScript('//assets.pinterest.com/js/pinit.js', function() {
            jQuery('#pin-it a').attr('href', '#');
            jQuery('#pin-it a').attr('onclick', "javascript:void((function(d){e=d.createElement('script');e.setAttribute('type','text/javascript');e.setAttribute('charset','UTF-8');e.setAttribute('src','//assets.pinterest.com/js/pinmarklet.js?r='+Math.random()*99999999);d.body.appendChild(e);}(document)));");
        });
    </script>
    

    That’s it. You should now get the bookmarklet action when the button is clicked, which will show a gallery of images on the page to choose from to use in your pin.

    Here’s an example implemented right here: image

    Anyway… Happy pinning!

     
  2. Magento: Automatically Apply a Coupon Code w/ jQuery

    I started working on planning a promotion a couple of days ago that would require a discount to be applied automatically. The site runs on Magento, which has very sophisticated coupon code and discounting tools. But this time, none of the built in options quite suited my needs.

    tl;dr; - I needed to apply a coupon code to a customer’s cart, before they actually had anything in their cart and without the visitor doing anything more than visiting a specific landing page. I was able to accomplish my goal with a little creativity, jQuery and determination. And, I don’t know PHP so this is a front end solution using jQuery and cookies. Read on for the code and how it works…

    How to Setup a Discount in Magento

    The first thing you’ll need to do is setup a coupon code with an associated discount of some type in Magento. It doesn’t really matter what type of coupon it is, you just need something to test with. I’m not going to explain how to do that here but for demonstration’s sake, I’ll assume that the coupon code you’ve chosen is testcouponcode and it gives the customer 20% off.

    Don’t be confused. Even though we’re setting up a coupon code, we’re not going to require any action from the visitor. We’re just going to use the back-end coupon capabilities of Magento to do the discounting in the shopping cart.

    Before we go any further, make sure you manually test the coupon code you setup by going into the cart and applying it the old fashioned way. Trust me that if you made a mistake, it’s much easier to discover and correct it at this stage.

    A Few More Notes About Coupons in Magento

    • When the customer enters a coupon code and clicks the apply coupon button, the code makes a HTTP POST to /checkout/cart/couponPost/ with the following data - remove=0&coupon_code=testcouponcode.
    • A coupon code cannot be applied to the shopping cart if there are no items in the visitor’s cart.
    • In my case, if the customer doesn’t checkout during the same session then I don’t want the discount to apply.

    With those three things in mind, let’s dive into how I’ve solved this problem.

    “Apply” Discount on the Landing Page, well Kinda…

    Remember the introduction - I want to apply a discount based on the landing page. However, in Magento, this is technically impossible. You cannot apply the coupon code to trigger the discount right away on the landing page. Since it’s the first page your visitor will have hit, the visitor will have zero items in their cart and that’s the problem.

    No worries though, there’s a way around that. All you have to do is create a session cookie that can be referenced if the visitor does end up putting items into his/her cart. The trigger will be the same (the landing page), but applying the discount will be technically deferred.

    To make the order of events easy to understand, here’s a list:

    1. Visitor hits the special landing page.
    2. Javascript on the page sets a special session cookie, signifying that a discount should be applied once an item has been added to the cart. This cookie will automatically be deleted when the session ends.
    3. Once the visitor views his/her cart (and there are one or more items in it), Javascript on the shopping cart page will make a POST to apply the coupon code that was specified back on that special landing page. This POST will happen in the background and then refresh the page quickly.

    Pretty simple right? Based on those three events, there will be two different blocks of code needed to make this work. One needs to be on the landing page and the other needs to be on the shopping cart view page.

    jQuery for the Landing Page

    Remember that the only goal for the code on this page is to set a session cookie that can be read and referenced later. I’m a newb when it comes to Javascript and for most things I use jQuery. Unfortunately there’s no built in way to write or read cookies in jQuery. The good news is that there’s a really great plugin for it and using the plugin is as easy as downloading it and including it in the HTML of your site. You can get it here, hosted on Google Code.

    Here’s the code:

    <script type="text/javascript" src="/path/to/js/jquery.cookies.2.2.0.min.js"></script>
    <script type="text/javascript">
        jQuery.cookies.set('nameOfCookie', 'testcouponcode', '/');
    </script>
    

    It’s really simple. All you have to do is name the cookie, give it a value and set the path that the cookie should be valid for. Setting a path of / means that it can be read or modified anywhere on the domain. I recommend making the value of the cookie equal to the actual coupon code. It’s most simple that way and I don’t think there’s any reason to try to hide it.

    Once you’ve downloaded the cookies plugin and included this code on your page, it’s a good idea to load the page and inspect your cookies to make sure the cookie was actually set. If it was, move on to the next step.

    jQuery for the Shopping Cart View Page

    This is the code you’ll need to put on the cart view page. The simplest way to implement this is to put it just before the closing <body> tag on the cart view page.

    <script type="text/javascript">
        var emptyCart = jQuery('.cart-empty').text();
        if ( (emptyCart == "") ) {
            jQuery.getScript('/path/to/js/jquery.cookies.2.2.0.min.js', function() {
                var couponCode = jQuery.cookies.get('nameOfCookie');
                if ( (/../i.test(couponCode)) && (!!couponCode) ) {
                    var postData = "remove=0&coupon_code=" + couponCode;
                    jQuery.post("/checkout/cart/couponPost/", postData, function() {
                        jQuery.cookies.del('nameOfCookie');
                        document.location = '/checkout/cart/';
                    });
                };
            });
        };
    </script>
    

    If you cannot add code to this page, you can place it in a file and include it in the head of the page. If you do that though, you’ll want to slightly modify it to execute after the page load is complete. You’ll also want to make sure that it only executes on the /checkout/cart/ page. It would look something like this…

    window.onload = function() {
        var emptyCart = jQuery('.cart-empty').text();
        if ( (/\/checkout\/cart\//i.test(page)) && (emptyCart == "") ) {
            jQuery.getScript('/path/to/js/jquery.cookies.2.2.0.min.js', function() {
                var couponCode = jQuery.cookies.get('nameOfCookie');
                if ( (/../i.test(couponCode)) && (!!couponCode) ) {
                    var postData = "remove=0&coupon_code=" + couponCode;
                    jQuery.post("/checkout/cart/couponPost/", postData, function() {
                        jQuery.cookies.del('nameOfCookie');
                        document.location = '/checkout/cart/';
                    });
                };
            });
        };
    };
    

    Here’s a quick explanation of what the code does:

    1. If you remember, a coupon code cannot be applied to a cart if there aren’t any items in the cart. The first part of the script grabs the text from the headline with a class equal to .cart-empty and it’s stored in the variable emptyCart.
    2. The following IF statement checks to make sure the emptyCart variable is empty. If it isn’t empty then that means the cart is empty and the rest of the code should not execute. If you used the second version, there’s also a condition that makes sure that the current document is the cart view page.
    3. Next, we need to make sure that the jQuery cookies plugin has been loaded.
    4. After the cookie plugin has loaded, the variable of couponCode gets set to the value of the coupon code from the landing page.
    5. Then there’s another IF statement that checks to see if a coupon code exists.
    6. If the coupon code does exist, then the next part makes a POST to /checkout/cart/couponPost/ with a query string equal to remove=0&coupon_code=testcouponcode which will actually activate the discount for the customer’s shopping cart.
    7. If that POST is successful, the cookie gets deleted and the page refreshes. The refresh is needed to let the customer see the discount in the sub-total. It’s unfortunate that the page refresh is necessary. I just haven’t been able to figure out a way to get around it. If you know how, please feel free to comment.

    I hope this has been helpful. Now, go forth and rockout some new landing pages!

     
  3. Fixing Magento’s Order Tracking in Customer Accounts

    Fixing may be too strong of phrasing. There’s nothing “broken” about how order tracking works inside customer account in Magento. However, it also isn’t very usable for several reasons. Let me explain.

    The Issues

    Here are the issues I have with how Magento presents tracking information in customer accounts.

    1. The link for the tracking number isn’t very well presented. If I placed an order and I’m logging into my account to check on it, I’m going to either be checking on the status or I’m trying to track the package. Presenting those two pieces of data should be top priority.

    2. When you find the link to get your tracking number and click on it, you’ll see that another pop up window opens just to show you the shipping carrier, method and tracking number. It seems like a big waste of a click to me. Why not show that information on the order detail page? Or, if you want to have a link to it, why not link directly out to the shipper’s tracking page?

    I set out to fix these to issues on sites that I manage.

    Uh Oh, I Don’t Know PHP or Magento

    It was very important to me to be able to fix this but I don’t know a thing about PHP or the core functionality of Magento. I wasn’t going to let that stop me though. I tried looking around in the interwebs to see if anyone else had already solved this problem and I didn’t find much. I also looked for extensions on Magento Connect, but no luck there either.

    After reviewing the problem and thinking about the existing skills that I posses, I decided it didn’t matter. All the data I need to solve those problems was available to me on the client side. And, since that’s true, I knew I could get what I needed by using jQuery.

    Using jQuery on Magento Sites

    I’ve been learning jQuery for about 6 months or so. I learn a little more each time I have a project or excuse to use it. Since I’ve started to use Magento, there has been a few circumstances where I wanted to use jQuery. The problem is that by default jQuery conflicts with Prototype (another Javascript framework). It took me a while to figure out what was going on but in the end the solution was simple.

    If Prototype.js is in use on a site, write your jQuery selectors like this jQuery('#someId').method() rather than $('#someId').method(). The reason is that both Prototype and jQuery use that same $ as a variable.

    orderTracking.js

    First thing’s first - here’s the script:

    jQuery(document).ready(function () {
        if ( jQuery('h2.table-caption a').length > 0 ) {
            var onClickActionObject = jQuery('h2.table-caption a').attr("onclick") + "";
            var onClickAction = JSON.stringify(onClickActionObject);
            var URLstripped = onClickAction.replace(/\"function.*\/\/www/, "https://secure").replace(/','.*/, "");
            jQuery.ajax({
                url: URLstripped,
                type: 'GET',
                dataType: 'text',
                success: function ( code ) {
                    var method = jQuery(code).find('.label').text();
                    var trackingNumber = jQuery(code).find('.value').text();
                    if (method.replace(/ .*/,"") == "USPS") {
                        var carrierURL = "https://tools.usps.com/go/TrackConfirmAction!execute.action?formattedLabel=";
                        var trackingURL = carrierURL + trackingNumber;
                    };
                    if (method.replace(/ .*/,"") == "UPS") {
                        var carrierURL = "http://wwwapps.ups.com/WebTracking/track?track=yes&trackNums=";
                        var trackingURL = carrierURL + trackingNumber;
                    };
                    jQuery('h2.table-caption span, h2.table-caption a').remove();
                    jQuery('.col-2 .box .box-content:first').after('<a id="external-tracking-link" target="_blank" style="color:#973C3E; font-weight:bolder;" href="' + trackingURL + '">Track Your Shipment</a>');
                }
            });
        };
    });
    

    Save the script and then include it in the <head> of your Magento site.

    Now, let me explain what’s going on step by step:

    1. The first step is to make sure the script doesn’t do anything until the page content has loaded. The first line in the script performs that task. Here’s the snippet jQuery(document).ready(function () { ... });.

    2. Since this script won’t serve any purpose except for when the customer is logged into their account, we don’t want the full script to execute on every page. There are multiple ways of doing this. One way would be to only include the script on the account pages but since I don’t know anything about the Magento’s core files I couldn’t go that route. I decided to include a simple if statement that would check to make sure that it doesn’t execute if not on the order details page. This is the test I used for my store if ( jQuery('h2.table-caption a').length > 0 ) { ... };.

    3. The “Track your order” link in the customer’s account has an onclick attribute assigned to it. When the customer clicks it, the popup window presents the carrier, shipping method and tracking number. We’ll need that information so we need to record that URL into a variable. In the script, that URL gets assigned to the URLstripped variable.

    4. Now, we need to get that tracking information by executing a AJAX request in the background and parse out the pieces we need. Lines 6-20 do the heavy lifting there. It’s important to note that we ship using either USPS or UPS. Therefore this script will only work for those two shipping carriers. You should be able to add in others pretty easily.

    5. The last step is to take the tracking number and rewrite the tracking link, style it and put it in a different spot on the page. That’s handled by lines 21 and 22.

    Now, when the tracking link is clicked on the carrier’s website will open in a new tab/window with the tracking information for the customer’s package already displayed.

    Let me know if you have any questions or have ideas for accomplishing this task in a better way.

    Thanks for reading.

     
  4. Blekko Inbound Links Extractor Bookmarklet

    Let’s get right to it. Start using the bookmarklet by dragging the following bookmarklet link to your bookmark toolbar.

    Blekko - Inbound Links

    1. Go to a Blekko Inbound Links SEO report like: ubuntuforums.com/ /domainlinks
    2. Click on the Blekko - Inbound Links bookmarklet from your toolbar.
    3. Watch for a new <textarea></textarea> to be added to the top of the page containing all the link data from the report.

    If you’re pulling data for a popular site like Twitter or Google, be prepared to be patient because there’s a shit ton of scraping going on. Average and/or small sites typically take a second or two. The example provided above takes only 3 or 4 seconds.

    On the topic of links…link to me or share it if you like it!

    The How and Why

    I’m still working to improve my jQuery skills. Recently I needed to automate some tasks relating to scraping data from a site. That particular site was built relying heavily on Javascript and Ajax requests, which meant that I couldn’t use my favorite combination of cURL, grep, cut and Bash to do the scraping. I needed something that would run client-side and execute Javascript.

    After thinking about it for a while, I decided I should learn about whether or not I’d be able to create a bookmarklet to do the job. I’ve been learning jQuery over the past few months, so I also wanted to make sure I could pair my developing jQuery skills with this bookmarklet project. I found this great post that provided the exact starting point I was looking for.

    After using the framework provided in that post, I was able to complete that project. In fact, the project ended up working so well that I started to wonder about other ways I could use this technique to automate daily tasks.

    Blekko’s SEO Inbound Links Report

    Very frequently, I use Blekko’s SEO tools to research sites. I’m a big fan of all the free information Blekko provides webmasters and search engine marketers. In particular, I use the Blekko Inbound Links report often. This report shows all the incoming links for any site that Blekko has in its index. It’s really easy to use and navigate. To see this report for yourself, you’ll need to create an account with Blekko (FREE).

    After your account is created, you can enter automateeverything.tumblr.com/ /domainlinks to see all the incoming links for this site. My blog is still very young (at the time of writing), so my link profile isn’t very impressive (you can help change that by linking to me!!). Here’s a screenshot of what you get by getting the same report for plus.google.com/ /domainlinks.

    So, here’s the rundown of what you’re seeing:

    • An overall summary of how many incoming links by how many unique domains.
    • A list of domains, sorted by most authority to least (as judged by Blekko’s “Host Rank”).
    • The count of how many links there are from each host/domain.

    While this is good summary information, I also want to know which pages on the domain the links are coming from, what the anchor text is, what page is being linked to and whether it’s a nofollow link or not. To get that extra detail, you have to click on each of the link counts for each linking domain. That will load a detailed list of links and will show all the information I mentioned above.

    Let me say again, I think it’s great that Blekko is providing all this information for free. But…I want more. I want it all in one place. I want it in Excel. I don’t want to try to combine browser addons and/or macros to get all this data. I don’t want to go to each individual from domain links page to get all the to, from and anchor link details. This would take a ton of time to do manually. This time would be far better spent actually analyzing the link data.

    At the time of writing, Blekko does not provide an export option. That’s why I’m going to do it for everyone!

    The Bookmarklet

    This bookmarklet will automate the entire process. In just a second or so, it will scrape all of the information on the page, request each “links” page, scrape all that and then finally output a nicely formatted report in a text box at the top of the page. From there you’ll be able to just copy and paste into Excel to be used in a pivot table, graph or whatever.

    I love Ubuntu, so I thought I’d do a demo on UbuntuForums.com to show the power of this bookmarklet.

    Like I said, easy and fast! Here’s a few sample lines from the output of the UbuntuForums.com report:

    From_Host   Host_Rank   Link_From_URL   Link_From_Anchor    nofollow    Link_To_URL
    www.43things.com    468.7   http://www.43things.com/things/view/2140/use-linux      nf  http://ubuntuforums.com/
    www.linuxjournal.com    1343.3  http://www.linuxjournal.com/content/post-penguicon-unity-unification-story          http://ubuntuforums.com/
    ubuntuforums.org    1167.3  http://ubuntuforums.org/showthread.php?t=520091&page=2351           http://ubuntuforums.com/bump.php?
    ubuntuforums.org    1167.3  http://ubuntuforums.org/showthread.php?t=635117 www.ubuntuforums.com        http://www.ubuntuforums.com/
    bugs.launchpad.net  653.8   https://bugs.launchpad.net/ubuntu/+source/evolution/+bug/349312     nf  http://ubuntuforums.com/
    bugs.launchpad.net  653.8   https://bugs.launchpad.net/ubuntu/+source/fglrx-installer/+bug/545257       nf  http://ubuntuforums.com/
    bugs.launchpad.net  653.8   https://bugs.launchpad.net/ubuntu/+source/fglrx-installer/+bug/545257/comments/6        nf  http://ubuntuforums.com/
    www.geek.com    927.7   http://www.geek.com/articles/geek-pick/microsoft-expertzone-training-teaches-best-buy-employees-about-linux-inferiority-2009097/        nf  http://www.ubuntuforums.com/
    www.ubuntugeek.com  257.5   http://www.ubuntugeek.com/avast-antivirus-for-ubuntu-desktop.html       nf  http://www.ubuntuforums.com/
    www.ubuntugeek.com  257.5   http://www.ubuntugeek.com/avast-antivirus-for-ubuntu-desktop.html/comment-page-2        nf  http://www.ubuntuforums.com/
    www.piratbyran.org  100.6   http://www.piratbyran.org/index.php?view=forum&a=thread&id=37918&fview=34           http://www.ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/    Official Ubuntu forums/suppport site        http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21953   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21959   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21969   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21974   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21985   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/www.43things.com    468.7   http://www.43things.com/things/view/2140/use-linux      nf  http://ubuntuforums.com/
    www.linuxjournal.com    1343.3  http://www.linuxjournal.com/content/post-penguicon-unity-unification-story          http://ubuntuforums.com/
    ubuntuforums.org    1167.3  http://ubuntuforums.org/showthread.php?t=520091&page=2351           http://ubuntuforums.com/bump.php?
    ubuntuforums.org    1167.3  http://ubuntuforums.org/showthread.php?t=635117 www.ubuntuforums.com        http://www.ubuntuforums.com/
    bugs.launchpad.net  653.8   https://bugs.launchpad.net/ubuntu/+source/evolution/+bug/349312     nf  http://ubuntuforums.com/
    bugs.launchpad.net  653.8   https://bugs.launchpad.net/ubuntu/+source/fglrx-installer/+bug/545257       nf  http://ubuntuforums.com/
    bugs.launchpad.net  653.8   https://bugs.launchpad.net/ubuntu/+source/fglrx-installer/+bug/545257/comments/6        nf  http://ubuntuforums.com/
    www.geek.com    927.7   http://www.geek.com/articles/geek-pick/microsoft-expertzone-training-teaches-best-buy-employees-about-linux-inferiority-2009097/        nf  http://www.ubuntuforums.com/
    www.ubuntugeek.com  257.5   http://www.ubuntugeek.com/avast-antivirus-for-ubuntu-desktop.html       nf  http://www.ubuntuforums.com/
    www.ubuntugeek.com  257.5   http://www.ubuntugeek.com/avast-antivirus-for-ubuntu-desktop.html/comment-page-2        nf  http://www.ubuntuforums.com/
    www.piratbyran.org  100.6   http://www.piratbyran.org/index.php?view=forum&a=thread&id=37918&fview=34           http://www.ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/    Official Ubuntu forums/suppport site        http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21953   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21959   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21969   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21974   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    www.forevergeek.com 122.7   http://www.forevergeek.com/2005/04/ubuntu_504_hoary_hedgehog_review/?replytocom=21985   Official Ubuntu forums/suppport site    nf  http://ubuntuforums.com/
    

    Notes on Usage

    • The bookmarklet is cross-browser and cross-platform. If you run into any compatibility issues at all, please drop me a comment with the details of your platform and the query in Blekko.
    • This bookmarklet works by calling the actual Javascript file from my public Dropbox folder. You can see the full code here: http://dl.dropbox.com/u/56691816/automateeverything/bookmarklets/blekko-inbound-links.js
    • Since this bookmarklet executes code that’s pulled from an external source, it’s functionality and feature set is subject to change. Not to freak you out but if I decided to do nasty things with the Javascript at some point in the future, the only way you’d know is if you inspected the file each time before running it. But, isn’t that the case with any software that updates its-self?

    I hope you enjoy it. Please feel free to let me know if you have feature requests or bugs.

    Enjoy and happy automation!