<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
    <title>
Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /Users/pretzelhands/Projects/php/pretzelhands.com/vendor/illuminate/support/helpers.php on line 262
</title>
    <link>https://pretzelhands.com</link>
    <description><![CDATA[
Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /Users/pretzelhands/Projects/php/pretzelhands.com/vendor/illuminate/support/helpers.php on line 262
]]></description>
    <atom:link href="https://pretzelhands.com/feed.xml" rel="self" type="application/rss+xml" />
    <language>
Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /Users/pretzelhands/Projects/php/pretzelhands.com/vendor/illuminate/support/helpers.php on line 262
</language>
    <lastBuildDate>Thu, 17 Jun 2021 09:10:21 +0000</lastBuildDate>

        <item>
        <title><![CDATA[How we made $2,500 by playing Animal Crossing]]></title>
        <link>https://pretzelhands.com/posts/making-2500-dollars-by-playing-animal-crossing</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/making-2500-dollars-by-playing-animal-crossing</guid>
        <description><![CDATA[The story of how my wife and I went from an idea to profitable side business in two months]]></description>
        <content:encoded><![CDATA[<p><x-disclaimer>
    <strong>Disclaimer:</strong> Everything described here was in flagrant violation of Nintendo's
    Terms of Use. I do not recommend doing this yourself as you may face a permanent
    ban of your console and potential legal trouble from Nintendo.
</x-disclaimer></p>

<p>One evening in January 2021, my wife came up to me and asked me:</p>

<blockquote>
  <p>Hey, I saw people are selling Animal Crossing items online. Could we do that too?</p>
</blockquote>

<p>Little did I know that I had just been nerd-sniped harder than I have been in the entire
past decade.</p>

<h2>Wait, what?</h2>

<p>Some of you may have come across the breakout hit of Nintendo Switch games in 2020 --
Animal Crossing: New Horizons! It's a wonderfully weird game about moving to an empty
island and building yourself a town to live in with anthropomorphic animals.</p>

<p>As part of this you collect a lot of items, furniture and crafting materials as well as
all your favorite cuddly villagers. Given the nature of this kind of game, it takes a long
time to get all the things you want. And where there's grinding, there's people profiting
from making the grind time shorter.</p>

<h2>Learning about treasure islands</h2>

<p>Before my wife asked me that all important question I mentioned above, I had learned that there
is such a thing as "treasure islands" in the Animal Crossing community. It's a simple concept:
You pay the owner of the island some money and you get to spend a certain amount of time on their
island. The island is usually filled with a ton of furniture and other items that the game has to
offer. During your scheduled time you can visit as often as you can possibly fit. Many sellers tend
to hang out on Facebook or Etsy.</p>

<p><x-image
    src="ac/1.jpg"
    caption="Demonstration of our treasure island"
/></p>

<p>Our goal was to set up one of those treasure islands ourselves. However doing so manually would mean
an unsustainable amount of work. Luckily we managed to gather some information from a seller that my
wife used to talk to and we had a rough game plan.</p>

<p>We'd need to modify save files on the Switch. The easiest way to do this is via homebrew software.</p>

<h2>Messing with a Switch and Homebrew</h2>

<p>I was lucky! I had done Homebrew hackery on a Nintendo DS Lite when I was a teenager. So this should be
easy, right?</p>

<p>Wrong. <strong>Wrong.</strong></p>

<p>Things have changed drastically since the olden days of inserting an SD
card into a modified game cart. For the Nintendo Switch, you have to exploit the recovery mode of the
integrated Nvidia Tegra processor. The annoying part is: The exploit only exists on older Nintendo Switch
consoles. I'm not going to describe the process here, but anyone curious can find out more on <a href="https://switch.homebrew.guide/">relevant sites.</a></p>

<p>Sadly, our existing Switch was not compatible, so we had a choice to make: buy a seperate, used Switch for
the price of a new one or stop right here. Personally I was rather hesitant as I didn't want to invest that
much money in what seemed like a very risky idea.</p>

<p>My wife, however, had just gotten her tax return and was more than willing to invest the money into the idea.
And so we went to our local classifieds website and shopped around. Eventually we found a compatible Switch
for the price of $350 and bought it. We christened him "Rodney".</p>

<p>As soon as the Switch and all the necessary equipment arrived, I got around to setting things up! First we
had to make backups of the Switch's existing OS with an Animal Crossing save file on it. This was fairly easy,
but took a relatively long time as the dump of the Switch takes up roughly 30GB.</p>

<p><x-image
    src="ac/2.jpg"
    caption="Very first Switch backup in progress"
/></p>

<p>Next, we had to find appropriate tools for decoding the backup, extracting/re-injecting the save file and
an Animal Crossing save editor. Here the GBATemp forums proved very helpful! All in all it took me about 2
days of fairly intense after-work fiddling to figure out the entire process.</p>

<h2>Setting up our Etsy store</h2>

<p>Since we now knew how to deal with the Switch and modifying the game itself, our next problem was figuring
out marketing and distribution. After doing some competitor research we figured that the best place to go
would probably be Etsy. We found that there was a small community of sellers there, so we had a pre-established
market. All of them had 50-100 sales logged in their account so clearly customers were willing to use Etsy
to get what they need. We just had to start our own little shop.</p>

<p>While I am decent enough at dealing with tech topics, I am not one for creative matters. This is where my
wife took over and started showing her prowess. In short order she created a brand identity, designed three
seperate islands using the save game editor and created marketing materials for it all. Within a week we
had our brand and our islands set up.</p>

<p>Say hello to <strong>Mama Tita's!</strong></p>

<p><x-image
    src="ac/3.jpg"
    caption="An overview map for one of our islands"
    alt="Marketing materials for Mama Tita's"
/></p>

<p>Setting up a shop on Etsy is a fairly painless process. Just fill in all necessary fields, add your bank
account to get the money and submit your tax information. Finally on February 10 we pushed the magical
"Go Live" button and our shop was out there! Exciting!</p>

<p>As further marketing measures we also established an Instagram account.</p>

<p>Our first order actually came in the very first night that we were putting ourselves out there! What a rush!
We got to setting up our island on the Switch and asked the person to come on over for their trip! She had
booked an hour, enjoyed a great time and we made our first $5!</p>

<p><x-image
    src="ac/4.jpg"
    alt="Our first order"
/></p>

<p>As it turned out the amount of marketing required on our part was actually very little. I'm not sure how
it works exactly, but Etsy did an amazing job of bringing in customers for us. This actually worked so well
that within two days we sold a total of 10 orders!</p>

<p><x-newsletter /></p>

<h2>Growing the store</h2>

<p>We kept hitting fascinating milestones at a breakneck speed. Within three days of launch we surpassed
$100 in revenue. Within six days of launching we passed 40 processed orders. Eight days after launch
we had made back our initial investment for the hackable Switch! Within 12 days we had gotten bestseller
status on Etsy! Two weeks in we had over 100 sales. It was freaking wild.</p>

<p>Then we started noticing that more and more customers were referred to us by friends and we started
being booked out for almost a week in advance. More and more this "side-business" of ours was taking
over our days. We spent a lot of time scheduling slots with customers, converting times from our local
UTC+1 to various other timezones and just supporting customers with any and all questions they had.</p>

<p>This became quite stressful, and we both spent a lot of time balancing between working on our real jobs and
dealing with customer requests as well as setting up the Switch. Although setting up the Switch was really
the smallest part of the job. But clearly, we needed a better way to deal with scheduling. If only one of us
was a programmer. Oh, wait..</p>

<p><x-disclaimer>
    As an aside, all of our traffic at this point was brought in either by Etsy or word of mouth. 
    We did zero marketing of our own.
</x-disclaimer></p>

<h2>Setting up a booking calendar</h2>

<p>After trying to setup a Calendly and deciding that it is way too complicated and restricted for our use case
I sat down, went to Namecheap and bought a domain. Time to build a scheduling calendar! I whipped up a Laravel
project together with Tailwind and Livewire and a day or two later we had the first version of our booking
calendar.</p>

<p><x-image
    src="ac/5.jpg"
    caption="Booking view and confirmation of our scheduling tool"
/></p>

<p>While in the first version the user still had to manually select their timezone this calendar got more and
more functionality. Eventually the timezone was read straight from the user's browser and the time was
automatically displayed in 12/24-hour time based on their preferences -- We had a few incidents with American
customers confusing 03.00 for 3pm instead of 3am. That was good fun!</p>

<p>Also just for fun I added a Telegram integration that would ping us every time a new reservation for a
treasure island visit came in. I had to mute that one fairly quickly.</p>

<h2>Getting too popular for our own good</h2>

<p>Around this time (we're talking roughly end of March), we had almost fully automated the most intense
parts of the business. At this point customers were picking their preferred time without any manual
intervention on our part and we were regularly booked out for days ahead. All we had to do was occasionally
reset the Switch and load in the new island. The total amount of effort this took was ~5 minutes per customer.</p>

<p>In general we were able to process between 12 and 16 orders every single day.</p>

<p>Since we were seeing so many sales come in, we eventually decided to open up our calendar from just 
a few days ahead to a whole month ahead. And people took to it like crazy. This lead to the peak point of
us having roughly 60 open orders in our Etsy account.</p>

<p>The problem with this is that Etsy has some seller protections in place. Many of our orders were marked
as overdue since people were booking weeks in advance. Eventually this lead to Etsy putting our shop on
a forced break as we "appeared to be struggling with fulfilling orders", even though we really weren't.</p>

<p>We learned at this point that one can update the shipping date in Etsy and we could have prevented this.
Oops! So we had to spend two weeks working through all our orders before Etsy re-enabled our shop. During
this time a few close friends also kept pushing us to increase prices, so as to reduce the workload and
increase profits. We fought them tooth and nail on this as the price range in this niche is very tight,
but eventually we did raise prices by about 40%.</p>

<p>When we came back with the new prices business picked up again straight away and while we had less orders,
we were still making roughly equivalent money. Better than we had feared!</p>

<h2>Getting shut down</h2>

<p>I wish I could tell you that we lived happy ever after running a massively successful shop on Etsy on the
side forever and ever. Sadly, Nintendo had a word to say about that and on April 24th we noticed that we
were receiving no orders in the evening, which was highly unusual. As we were rather tired that day we didn't
mind so much and just went on our merry way.</p>

<p>Then we received a message from a customer: "Hey, where can I book my island trip?". We informed them to
just purchase one of the available Etsy listings.</p>

<blockquote>
  <p>Okay, but I don't see any listings on your profile</p>
</blockquote>

<p>Wait, <strong>WHAT!?</strong> We immediately checked into our shop page and it was true! No more listings on our profile.
Etsy had taken them down! We were extremely confused as to what happened, because there was no notice in the UI.
So I told my wife to check her email for any message from Etsy.</p>

<p>And there was a new email.</p>

<blockquote>
  <p>Information regarding a DMCA infringement</p>
</blockquote>

<p>Oh, no. We always knew that our business operated in a shady area in regards to Nintendo's Terms of Service.
We knew that they might actually notice our shop if it gets too successful. But it seemed like a distant
reality. But here it was.</p>

<p>The email was quite polite and in it Etsy informed us that they had taken down our listings due to a copyright
claim by Nintendo of Europe. From what we were able to tell they weren't approaching this from the angle of
us violating their terms, but rather they took us down through the fact that we seemingly used protected materials
to advertise our service.</p>

<p>At this point we had a discussion and decided that we don't want to risk any further trouble with Nintendo.
It wasn't an easy choice, but we decided to refund all remaining orders and end the shop there, before things
became serious.</p>

<h2>Final thoughts</h2>

<p>This is the story of Mama Tita's. The shop ran for a total of <strong>2 months and 14 days</strong>. In that time we made
<strong>$2,386</strong> in revenue. If you subtract our initial investment in comes out to a fairly even <strong>$2,000</strong> in profits.</p>

<p>It was an incredibly wild ride and it showed me that you can find success in the most unexpected places.
And while we do miss the shop and the joy we brought our customers, we were also rather glad. Being "always on"
for customer requests can be quite exhausting, even if most things are automated.</p>

<p>I'm grateful that we were able to do this and for all the customers who became friends along the way. And
next time my wife comes to me with a wild business idea, I'll definitely join her again!</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Thu, 17 Jun 2021 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Selling one-time purchase products with Paddle]]></title>
        <link>https://pretzelhands.com/posts/selling-one-time-purchase-products-with-paddle</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/selling-one-time-purchase-products-with-paddle</guid>
        <description><![CDATA[Learn how to setup Paddle and use it to let users to buy a one-time payment product (e.g. a video course)]]></description>
        <content:encoded><![CDATA[<p>Have you ever wanted to sell something online? Maybe you've built a cool product.
Perhaps you're the leading expert on patting alpacas, and you want to sell your
cool book containing your secret technique for the world to see.</p>

<p>These days you have many options. Artisanal crafts are popular on Etsy! You can sell
digital products on Gumroad. I heard some people actually still use Ebay to sell their
used stuff. But for the sake of this article, let's assume you don't want to hand over
your business to some platform. You want a custom website, where the only thing standing
between you and your customer is the payment provider. So let's talk about why you
might want to use Paddle!</p>

<h2>Why Paddle?</h2>

<p>Paddle is a platform that acts as a so-called <code>Merchant Of Record</code>. This is a very fancy
legal term for saying, that Paddle handles everything related to payments for you. They
process your customers payments, they handle the tax obligation and in the end they send
whatever money you've earned to your bank account.</p>

<p>If you're conducting your business from the European Union, this is a very nice thing to
have. The reason is that handling your own taxes in the EU is for all intents and purposes
a mess. You can read <a href="https://europa.eu/youreurope/business/taxation/vat/vat-digital-services-moss-scheme/index_en.htm">a detailed guide</a>
if you have some time to spare.</p>

<p>The short summary is: For all digital services you sell within the EU you have to
track which country your customer comes from, apply one of 27 different tax rates, or
maybe no tax rate at all, track evidence of where your user came from and then file all
of this with your country's financial authority, again split up by country. Super fun. 
If you want to go do that Miguel Piedrafita wrote a <a href="https://miguelpiedrafita.com/laravel-cashier-vat">great guide</a>
on implementing VAT with Stripe for Laravel. This at least takes care of the technical
side of things.</p>

<p>For lazy people like me, Paddle handles all this. You end up receiving a single
payout every month. Since I despise dealing with bureaucracy, that seems like
an awesome deal to me.</p>

<h2>Setting up Paddle</h2>

<p>To get started with Paddle, you'll need to setup an account with them. To do that,
visit their <a href="https://paddle.com/demo">sign up page</a> and fill out their getting started form.
They should get back to you fairly quickly.</p>

<p>Once you have a Paddle account we'll need two things: Your vendor ID and a product ID.
To get your vendor ID, visit the <code>Authentication</code> page under <code>Developer Tools</code>. You'll
find it at the very top.</p>

<p><x-image
    src="paddle/01.png"
    caption="These are not secret, by the way."
    alt="Paddle Vendor ID"
/></p>

<p>Next we'll have to create a product. You can find this under <code>Catalog &gt; Products</code>.
You can either create the actual product you intend to sell, or just a dummy product
for now.</p>

<p><x-image
    src="paddle/02.png"
    alt="Product creation screen"
/></p>

<p>For now you can select the "Download" fulfillment option. This will send a download 
link for your product to the customer once they have purchased it. This is also the
option that is relevant if you want to generate custom licenses for your product.
(More on that in a future post.)</p>

<p>After you have created a product, you should see it in your overview and be able to
copy its ID.</p>

<p><x-image
    src="paddle/03.png"
    caption="Again, no secrets here."
    alt="Product overview screen"
/></p>

<p>Once you have both your vendor ID and your product ID, we're ready to move on to
the technical parts.</p>

<h2>Setting up the project</h2>

<p><x-disclaimer>
    (This part is optional if you already have a website prepared.)
</x-disclaimer></p>

<p>As with many other projects on my blog, I'll base it on the little <a href="/posts/establishing-a-base">boilerplate project</a>
that I made. It gives us a common starting ground, as explained in the article.
Let's clone it from GitHub:</p>

<pre><code class="language-text">$ git clone git@github.com:pretzelhands/base.git
</code></pre>

<p>And install any dependencies that are pre-delivered.</p>

<pre><code class="language-text">$ composer install
</code></pre>

<p>You will also want to create a file called <code>.env</code> in the project root
It can remain empty for now.</p>

<p>As we'll be dealing with visual things this time, I'll also pull in Tailwind.
Opinions about it are divided, but I am not good at design and Tailwind is my
magic stick to make things look .. okay.</p>

<p>First, let's create a new <code>assets/</code> directory in the <code>public/</code> directory.
Then to set up Tailwind, we can use the <code>npx</code> tool pre-delivered with <code>npm</code>.</p>

<pre><code class="language-text">$ npx tailwindcss-cli build -O public/assets/style.css                                                                                                                                                                           1 changed file  main

   tailwindcss 2.0.3

   🚀 Building from default CSS... (No input file provided)

   ✅ Finished in 2.24 s
   📦 Size: 3.74MB
   💾 Saved to public/assets/style.css
</code></pre>

<p>This will put all available Tailwind styles into <code>public/assets/style.css</code>.
As you can see that file is 3.74MB, that's huge! We'll leave it for now, but
at the end I'll show you how to reduce this down to a few kilobytes.</p>

<p>Next we'll need to setup some kind of checkout page. For this I'll make a new
PHP file in <code>public/</code> called <code>checkout.php</code>. In it I'll set up an HTML skeleton
and include our stylesheet.</p>

<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;

    &lt;link rel="stylesheet" href="/assets/style.css"&gt;

    &lt;title&gt;Paddle Integration&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;!-- Our checkout goes here --&gt;

&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>Note that the path to the stylesheet is relative to <code>public/</code> because that is
the directory we serve on our server. Now let's get the server up and running!</p>

<p>The repository we are building from already includes a script for that.</p>

<pre><code class="language-text">$ composer run dev
</code></pre>

<p>This will start a web server on <code>localhost:8080</code> and you the included <code>expose</code> tool
will get you a publicly shareable link. When you open it, you should see a page
displaying nothing but a pretzel. If you visit <code>checkout.php</code> you should see an
empty screen.</p>

<h3>Our checkout screen</h3>

<p>For the purposes of this tutorial I pre-built a checkout screen you can copy for
following along. Again, I am just a code monkey wielding a magic Tailwind stick, so 
it doesn't look like the most amazing thing ever. But it works for our purposes.</p>

<pre><code class="language-html">&lt;!-- public/checkout.php --&gt;

&lt;body class="min-h-screen flex justify-center items-center"&gt;
&lt;div class="w-full h-1/2 absolute top-0 bg-gray-100"&gt;&lt;/div&gt;

&lt;div class="relative z-10 flex w-full max-w-4xl bg-white shadow-xl rounded-lg"&gt;
    &lt;div class="w-3/5 p-16 space-y-8 flex flex-col justify-center"&gt;
        &lt;h1 class="font-black text-3xl text-gray-900"&gt;
            &lt;svg class="inline-block text-white rounded-full bg-indigo-500 -mt-2 mr-1 p-1 w-8 h-8" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"&gt;
                &lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" /&gt;
            &lt;/svg&gt;
            Amazing Product
        &lt;/h1&gt;

        &lt;p class="text-gray-600 leading-relaxed"&gt;
            You will not believe how much better your life will be when you purchase this product.
            Yes, it's actually that good. There is no better way to improve your life.
        &lt;/p&gt;
    &lt;/div&gt;


    &lt;div class="flex flex-col flex-grow justify-center items-center space-y-6 p-8 bg-gray-50"&gt;
        &lt;p class="text-xl text-center"&gt;Pay once,&lt;br&gt;keep it forever&lt;/p&gt;
        &lt;p class="text-7xl text-gray-900 font-black"&gt;$149&lt;/p&gt;

        &lt;div class="text-center space-y-1"&gt;
            &lt;button id="js-checkout-button" class="px-6 py-3 bg-gray-800 hover:bg-gray-900 text-xl text-white rounded-lg"&gt;
                Buy the awesome
            &lt;/button&gt;

            &lt;p class="text-gray-600 text-sm"&gt;Local taxes may be added&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
</code></pre>

<p>Open up <code>localhost:8080/checkout.php</code> (or your <code>expose</code> URL) and you should see
the checkout screen. Here's an idea of what it should look like.</p>

<p><x-image
    src="paddle/04.png"
    alt="Checkout screen"
/></p>

<p>Now after all this setup we can finally integrate Paddle and make our product
purchasable. This is the exciting part of the article!</p>

<h2>Integrating the Paddle checkout</h2>

<p>By default, Paddle works as a small overlay that activates itself when you click
on a trigger (say, a button). To integrate it, first we need the Paddle.js script
which we load directly from them!</p>

<p>Add the following just before your closing <code>body</code> tag.</p>

<pre><code class="language-html">&lt;script src="https://cdn.paddle.com/paddle/paddle.js"&gt;&lt;/script&gt;
</code></pre>

<p>Now we're ready to start interacting with Paddle. We're just two small steps away
from handling purchases!</p>

<p>Our next step is to initialize Paddle. To do so we need the vendor ID we copied
earlier. Then we add another <code>&lt;script&gt;</code> tag in which we call <code>Paddle.Setup</code> like so:</p>

<pre><code class="language-html">&lt;script src="https://cdn.paddle.com/paddle/paddle.js"&gt;&lt;/script&gt;
&lt;script&gt;
    Paddle.Setup({ vendor: 30544 })
&lt;/script&gt;
</code></pre>

<div class="remark">
<p>
You can also do this in a separate JavaScript file, to keep things clean.
</p>
</div>

<p>With this Paddle is now ready to handle our checkout. It doesn't assume anything
about our setup, so we have to let Paddle know when it's time to jump into action
In our case that would be once the user clicks our "Buy the awesome" button.</p>

<p>To do this we can use vanilla JavaScript to attach an event listener. That looks
like this:</p>

<pre><code class="language-html">&lt;script src="https://cdn.paddle.com/paddle/paddle.js"&gt;&lt;/script&gt;
&lt;script&gt;
    Paddle.Setup({ vendor: 30544 })

    document.querySelector('#js-checkout-button').addEventListener('click', () =&gt; {
        Paddle.Checkout.open({ product: 644761 })
    })
&lt;/script&gt;
</code></pre>

<p>And believe it or not, that's all you technically need to do! Once you click your
checkout button, you should see the little Paddle modal pop up and ask you to enter
your purchase details.</p>

<p><x-image
    src="paddle/05.png"
    alt="Paddle Purchase model"
/></p>

<p>Paddle also automatically adds the appropriate amount of VAT and asks your customers
for their VAT ID, should they have one. Neat! We could wrap up here and everybody 
would be super happy, right? Yeah, but let's look a bit deeper.</p>

<p>Paddle supports webhooks! These are mostly useful for subscriptions (which we'll
cover in the future), but you can also do fun things with them for single purchases.</p>

<p>For example I <a href="/posts/build-a-telegram-bot-in-php">built a Telegram bot</a> that summarized
my daily earnings from Notebag and let me know at the end of the day. Or you could
build a little business dashboard for yourself. The possibilities are only
limited by your creativity.</p>

<p><x-newsletter /></p>

<h2>Interacting with webhooks</h2>

<p>To get started with webhooks from Paddle we need to do two things: We need to set them
up in the vendor interface and we need to grab our public key. To activate webhooks
you can go to <code>Developer Tools &gt; Alerts/Webhooks</code>.</p>

<p>There you will be able to specify the URL to which your webhooks will be sent.
You can also set an email if you want to receive regular email notifications.
I will set the URL to the one that <code>expose</code> provided to me, and use a <code>webhooks.php</code>
endpoint.</p>

<p><x-image
    src="paddle/06.png"
    alt="Webhook settings"
/></p>

<p>Below those settings you can enable any webhooks you want. I would recommend leaving
them unchecked until you go live with your integration. For testing things we will
use the Webhook simulator at the top of the page.</p>

<p>Once you have set your webhook URL, you should grab your public key from 
<code>Developer Tools &gt; Public Key</code> and put it in the <code>.env</code> file. I called it
<code>PADDLE_PUBLIC_KEY</code>.</p>

<pre><code># .env

PADDLE_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIICIthisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisicanyouseemethisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapublickeythisisapublickeythisisapubl
ickeythisisapublickeythisisapubl
-----END PUBLIC KEY-----"
</code></pre>

<div class="remark">
<p>
.env files support multiline strings, so you can just paste in the block as it is
</p>
</div>

<p>Anyone who has dealt with public-key cryptography probably knows where this is going.
For those who aren't familiar: We will use this key to verify that the messages that
are sent to our <code>/webhooks.php</code> endpoint are actually sent by Paddle. This is a good
thing™ to do.</p>

<h3>Verifiying webhooks</h3>

<p>Now that we have our public key, we can use it to verify HTTP requests arrived from
Paddle. Since this is a somewhat long-winded process, we'll extract it to a helper
function. So let's open up <code>src/helpers.php</code> and add a new function called
<code>verifyPaddleWebhook</code>.</p>

<pre><code class="language-php">// src/helpers.php

&lt;?php

// ..snip..

function verifyPaddleWebhook(array $request) {
    $publicKey = env('PADDLE_PUBLIC_KEY');
    $signature = base64_decode($request['p_signature']);

    $fields = $request;
    unset ($fields['p_signature']);

    ksort($fields);

    $data = serialize($fields);

    return openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA1);
}
</code></pre>

<p>Let's dissect that function bit by bit. First we grab our public key from our .env
file and decode the request signature that Paddle sent us. Next we copy all the
request values over to a new fields variable and remove the <code>p_signature</code> field,
as it is not included in the message we want to verify.</p>

<p>Then Paddle requires us to sort all array fields alphabetically by key. PHP once
again makes this easy enough to do with <code>ksort()</code>. Once we have the sorted array,
we serialize it into a string.</p>

<p>This string can then be verified using the <code>openssl_verify</code> function. For this we
need the string we want to verify, the signature, our public key and we use SHA-1
as algorithm. OpenSSL then does its magic and tells us whether the message was
encrypted by the private key that pairs up with our public key. So we return
that as result. And presto! We now have a boolean that indicates whether the request
comes from Paddle or no.</p>

<p>So let's make use of it in our <code>webhooks.php</code> endpoint!</p>

<pre><code class="language-php">// public/webhooks.php

&lt;?php

require __DIR__ . '/../bootstrap.php';

if (!verifyPaddleWebhook($_POST)) {
    die();
}

if ($_POST['alert_name'] === 'payment_succeeded') {
    echo sprintf('🥨 %s just bought our thing!', $_POST['customer_name']);
}
</code></pre>

<p>To get everything sorted, we need to include the <code>bootstrap.php</code> file that sets up
our application. Then we pass in the <code>$_POST</code> superglobal to the verification function
we just wrote, since Paddle passes everything through that.</p>

<p>If the verification fails, we just end execution right there. We don't want to tell
anybody trying to be smart if this worked or not. We just give them nothing.</p>

<p>After this, you have free rein of what to do. You can save the transaction in
your database and build yourself a small dashboard. You can send yourself a
notification in Telegram/Slack/etc. But before we do all that, we should test our
implementation!</p>

<h2>Testing our webhook with the simulator</h2>

<p>To test our little webhook handler that could, switch back over to the Paddle
dashboard and open the Webhook Simulator under <code>Developer Tools &gt; Alerts / Webhooks &gt; Webhook Simulator</code>.</p>

<p><x-image
    src="paddle/07.png"
    alt="Paddle Webhook simulator"
/></p>

<p>There you can choose what event to simulate. For now, let's go with "Payment Succeeded".
If you have configured your webhook URL earlier Paddle will fill it out for you already.
Otherwise enter the URL you received when you ran <code>composer run dev</code> and add <code>/webhooks.php</code>
at the end.</p>

<p>Paddle also generates a lot of dummy data that you can customize to your hearts content.
Once you're ready, you can click the "Call Webhook" button at the bottom and see the
fruits of your hard labour.</p>

<p>Congratulations on successfully integrating a Paddle Checkout onto your website!</p>

<h2>Closing thoughts</h2>

<p>As you can see the basic process of integrating Paddle for a single purchase
product is not that complicated. From here on out you can use it to sell anything
you like online!</p>

<p>In other posts in the future we will take a look at how to integrate subscriptions
and how to generate our own license codes so we can make our own licensing system!</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Fri, 12 Feb 2021 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Build your own PSR-4 autoloader]]></title>
        <link>https://pretzelhands.com/posts/build-your-own-psr-4-autoloader</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/build-your-own-psr-4-autoloader</guid>
        <description><![CDATA[Learn how to build your own autoloader and de-mystify what Composer actually does behind the scenes]]></description>
        <content:encoded><![CDATA[<p><x-disclaimer>
    You can find the repository for this project <a href="https://github.com/pretzelhands/psr-4">on GitHub</a>
</x-disclaimer></p>

<p>If you're anything like me, you've probably wondered before: "What kind of magic 
makes Composer go?" - You look at the source code of <code>vendor/autoload.php</code> and
see it links to yet another autoloader that's called <code>autoload_real.php</code> Then
there's a bunch of hashes and it's confusing and eventually you just accept that
Composer is magic and you aren't worthy of its secrets.</p>

<p>Today, let's look at how you can build your own Composer. At least the autoloading
part. We won't talk about the seven million other features that Composer provides to
us aside from that.</p>

<h2>Setting up the project</h2>

<p>This time, all you need to bring is any kind of PHP installation. Technically 
it needs to be at least PHP 5.3, but I very much hope that you aren't running
anything that old.</p>

<p>As always, we'll start with what we want our project structure to look like</p>

<pre><code class="language-txt">psr-4/
├─ autoloader.php
├─ app.php
├─ src/
│  ├─ Pretzel/
│  │  ├─ RandomClass.php
</code></pre>

<p><code>autoloader.php</code> is going to be what it says. This file will be roughly equivalent
to <code>vendor/autoload.php</code> when using Composer.</p>

<p><code>app.php</code> is going to be our test file where we include the autoloader and try
to use our classes.</p>

<p><code>src/</code> will contain our application-specific code. The folder doesn't have to
be Pretzel. You pick whatever makes you happy.</p>

<p>Our stated goal is to load every class that is located in <code>src/Pretzel</code>. Each subdirectory
constitutes a new namespace. So any file in <code>src/Pretzel/Factory</code> has a namespace of <code>Pretzel\Factory</code>.</p>

<h2>The magic spice: spl_autoload_register</h2>

<p>All we really need to create an autoloader is that function: <code>spl_autoload_register</code>.
You can read more details about it in <a href="https://www.php.net/manual/en/function.spl-autoload-register.php">the PHP documentation</a>.</p>

<p>You call it like this</p>

<pre><code class="language-php">&lt;?php

spl_autoload_register(function(string $class) {
    // Perform unknowable magic here
});
</code></pre>

<p>All you need to pass in is a <a href="https://www.php.net/manual/en/language.types.callable.php">callable</a>
of some kind that receives one argument: a string with a class name. You can register
multiple of these callbacks too. This is useful if you want to have a few logically 
different ways to load classes.</p>

<p>After your function is registered, PHP will call it every time a class is used for
the first time. This is usually either when you call <code>new</code> on it, or when you use
a static function on it. Let's see that in action.</p>

<h2>Interacting with spl_autoload_register</h2>

<p>I'll set up my <code>autoloader.php</code> like this:</p>

<pre><code class="language-php">// autoloader.php

&lt;?php

spl_autoload_register(function(string $class) {
    die($class)
}
</code></pre>

<p>For now we'll just print the class name that was desired and exit.
We also need a class to load, so let's build a quick <code>PretzelFactory</code>. It
returns a pretzel emoji from a static method. It's very fancy.</p>

<pre><code class="language-php">// src/Pretzel/Factory/PretzelFactory

&lt;?php

namespace Pretzel\Factory;

class PretzelFactory {
    public static function getPretzel(): string
    {
        return '🥨';
    }
}
</code></pre>

<p>We need to use that class somewhere. This is what our <code>app.php</code> file is for.
We'll have to include our autoloader, pull the class in and then call the static
method. Something like this.</p>

<pre><code class="language-php">// app.php

&lt;?php

require __DIR__ . '/autoloader.php';

use Pretzel\Factory\PretzelFactory;

echo PretzelFactory::getPretzel();
</code></pre>

<p>When we run our <code>app.php</code> file, this is the result.</p>

<pre><code class="language-bash">$ php app.php
Pretzel\Factory\PretzelFactory
</code></pre>

<p>So we get the fully-qualified class name (<code>FQCN</code>) passed to us. Now <a href="https://www.php-fig.org/psr/psr-4/">PSR-4</a>
states that a FQCN name consists of:</p>

<ul>
<li>A namespace prefix (e.g. <code>Pretzel</code>) that corresponds to a base directory (e.g. <code>src/Pretzel</code>)</li>
<li>Zero or more sub-namespaces (e.g. <code>Factory</code>) that correspond to a directory (e.g. <code>src/Pretzel/Factory</code>)</li>
<li>One class name corresponding to a PHP file with that same name (e.g. <code>PretzelFactory.php</code>)</li>
</ul>

<p>Some of you might have realized by now, that our FQCN is essentially a direct path to the file
we need to load. How convenient!</p>

<h2>Loading the appropriate files</h2>

<p>To load our file, first we need to turn our FQCN into an actual path. This can
be done by replacing every backslash with a forward slash and appending <code>.php</code> at the
end. With that, we can already autoload files from a hard-coded base path!</p>

<p>Let's try it out!</p>

<pre><code class="language-php">// autoloader.php

&lt;?php

function fqcnToPath(string $fqcn) {
    return str_replace('\\', '/', $fqcn) . '.php';
}

spl_autoload_register(function (string $class) {
    $path = fqcnToPath($class);

    require __DIR__ . '/src/' . $path;
});

</code></pre>

<p>After adding this, we can just run our <code>app.php</code> script again</p>

<pre><code class="language-bash">$ php app.php
🥨
</code></pre>

<p>Great success! This shows you what autoloading boils down to: A fancy way to 
require a PHP file just in time when it's needed. But if we want to be (reasonably)
spec-compliant, we still have work to do.</p>

<p><x-newsletter /></p>

<p>Right now the path we load from is hard-coded to be <code>src/</code> but according to
PSR-4, every package is free to define its own namespace prefix that maps to
a path. This is essentially what you do every time you set up autoloading in
Composer</p>

<pre><code class="language-json">{
    "autoload": {
        "psr-4": {
            "Pretzel\\": "src/Pretzel"
        }
    }
}
</code></pre>

<p>So let's try to parse this JSON snippet and use it to load our classes.
I'll call it <code>conductor.json</code>, because that's what the logo of Composer
depicts, and we're basically a cheap knock-off. 🤪</p>

<h2>Autoloading according to configuration</h2>

<p>The first thing we have to do is to grab our JSON file and turn it into
something we can actually work with (i.e. an associative array). Luckily,
PHP has native functions for both of those things.</p>

<pre><code class="language-php">// autoloader.php

&lt;?php

$configuration = json_decode(
    file_get_contents('conductor.json'),
    true
);

$namespaces = $configuration['autoload']['psr-4'];

// .. SNIP ..
</code></pre>

<p>Now, the user is free to specify as many namespaces and base directories
as she wants, so we need to check if the prefixed/first namespace is available
in our <code>psr-4</code> associative array. We could use a <code>foreach</code> loop, but we can
also make it a bit more efficient using a function called <code>strtok()</code>.</p>

<pre><code class="language-php">// autoloader.php

&lt;?php
// .. SNIP ..

spl_autoload_register(function (string $class) use ($namespaces) {
    $prefix = strtok($class, '\\') . '\\';

    // We don't handle that namespace.
    // Return and hope some other autoloader handles it.
    if (!array_key_exists($prefix, $namespaces)) return;

    $baseDirectory = $namespaces[$prefix];
});
</code></pre>

<p>What <code>strtok</code> does is tokenize the string according to a separator. If we pass
in the backslash, it will return us everything up to the first backslash, excluding
that. To correctly match our configuration, we just add it again ourselves.</p>

<p>For the performance nerds among you this makes sure that we find the proper
base directory in <code>O(1)</code> time.</p>

<p>Then we check if this namespace prefix maps to a path in our configuration.
If it does, great! If it doesn't, we just return from the function and hope that
someone else handles this case. We can't do anything else with it.</p>

<p>The next step is to remove the prefixed namespace from our class name. This is
because we know exactly what directory the prefix maps to, and we want to start
building our final path from that. We can do this in our <code>fqcnToPath</code> function.</p>

<pre><code class="language-php">// autoloader.php

&lt;?php
// .. SNIP ..

function fqcnToPath(string $fqcn, string $prefix) {
    $relativeClass = ltrim($fqcn, $prefix);

    return str_replace('\\', '/', $relativeClass) . '.php';
}

spl_autoload_register(function (string $class) use ($namespaces) {
    $prefix = strtok($class, '\\') . '\\';

    // We don't handle that namespace.
    // Return and hope some other autoloader handles it.
    if (!array_key_exists($prefix, $namespaces)) return;

    $baseDirectory = $namespaces[$prefix];
    $path = fqcnToPath($class, $prefix);
});
</code></pre>

<p>Once again PHP has our back! We use <code>ltrim</code> to remove the main namespace from
the beginning of our FQCN. This gives us the "relative" class, in the same sense
as having a relative path. The rest of the function stays the same.</p>

<p>At this point we have a base directory and a relative path going out from that
base directory. So the final piece that's missing is trying to require the 
appropriate file!</p>

<pre><code class="language-php">// autoloader.php

&lt;?php
// .. SNIP ..

spl_autoload_register(function (string $class) use ($namespaces) {
    $prefix = strtok($class, '\\') . '\\';

    // We don't handle that namespace.
    // Return and hope some other autoloader handles it.
    if (!array_key_exists($prefix, $namespaces)) return;

    $baseDirectory = $namespaces[$prefix];
    $path = fqcnToPath($class, $prefix);

    require $baseDirectory . '/' . $path;
});
</code></pre>

<p>At this point we can run our <code>app.php</code> again and see if it works as advertised.</p>

<pre><code class="language-bash">$ php app.php
🥨
</code></pre>

<p>Great! So now we can add PSR-4 autoloading configuration to our JSON file just as
if it were Composer. Of course, one should probably add some more input validation
to this code. Users could forget to specify a <code>\</code> in their namespace. Or they could
accidentally add a <code>/</code> in the base directory. But I'll leave that part to you, dear
reader.</p>

<h2>Closing thoughts</h2>

<p>While this is a simplified example of what Composer does internally, it still
shows you the general principles of PSR-4 autoloading and what goes into it. 
Once you break it down, it's really not as complex as it seems!</p>

<p>The main reason I even got the idea was due to a side project I'm silently
hacking away on. I needed a simple way to load in PHP classes that define plugins,
and well, here we are.</p>

<p>As always, the entire code for this blog post is <a href="https://github.com/pretzelhands/psr-4">available on GitHub</a>.</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Tue, 26 Jan 2021 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Establishing a base for the future]]></title>
        <link>https://pretzelhands.com/posts/establishing-a-base</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/establishing-a-base</guid>
        <description><![CDATA[Setting up a basic PHP project for future posts on this blog. So we're all talking about the same thing]]></description>
        <content:encoded><![CDATA[<p><x-disclaimer>
<strong>TL;DR:</strong> This post describes the structure behind my recently published 
<a href="https://github.com/pretzelhands/base">pretzelhands/base</a> repository.</p>

<p>For any seasoned PHP developer, looking at the repository should be enough.
If you're interested in the rationale, feel free to read along.
</x-disclaimer></p>

<p>This base project is intended as the common ground for any future projects built
as part of this blog. For small projects it can feel over the top to bring along
a whole <a href="https://laravel.com">Laravel install</a> when a much smaller framework
would do.</p>

<p>It also ensures that no one needs to be deep into any one ecosystem, unless it is
explicitly mentioned.</p>

<p>Anybody who has recently read <a href="/posts/build-a-telegram-bot-in-php">my Telegram bot post</a>
will already recognize the project structure.</p>

<h2>Requirements</h2>

<ul>
<li>PHP 7.4, ideally PHP 8.0</li>
<li>Composer</li>
</ul>

<p>We explicitly do <em>not</em> need a local web server such as Apache or nginx. We'll
use a special command to get a shareable link for our project.</p>

<h2>Creating the project structure</h2>

<p>As any modern PHP project should, the app uses Composer to define and install
dependencies. So let's set that up.</p>

<pre><code class="language-bash">$ composer init
</code></pre>

<p>And for the basic setup we only require one other dependency. That's <code>vlucas/phpdotenv</code>.
We use it to load sensitive data from a <code>.env</code> file. Let's install it!</p>

<pre><code class="language-bash">$ composer require vlucas/phpdotenv
</code></pre>

<p>Then I want to create a folder structure that looks something like this</p>

<pre><code class="language-txt">base-project/
├─ vendor/
├─ composer.json
├─ composer.lock
├─ .env
├─ bootstrap.php
├─ src/
│  ├─ Pretzel/
│  ├─ helpers.php
├─ public/
│  ├─ index.php
</code></pre>

<p>In this structure we first have the files that Composer generates. The <code>vendor/</code>
folder for our dependencies and autoloader, the <code>composer.json</code> defining our
project meta data and dependencies and the <code>composer.lock</code> file pinning our
dependencies to specific versions.</p>

<p><code>.env</code>  holds our sensitive data that can change between your local computer 
and your actual server. An example would be an API key to access GitHub or
a key that the application uses for encryption.</p>

<p><code>bootstrap.php</code> pulls in the Composer autoloader and sets up
the loading of the <code>.env</code> files. It's used for any setup tasks that always
need to run. We will usually include it in the PHP files located in <code>public/</code></p>

<p><code>src/Pretzel</code> contains our application specific code. Anything that makes your idea
come to live! The folder name is based on the namespace. This will defined later
and you can change it to anything you like.</p>

<p><code>src/helpers.php</code> contains small helper functions that we want to use every
now and again. For example, we'll define a convenience function for getting
values from our <code>.env</code> file.</p>

<p><code>public/</code> contains all the things we want to expose to the scary internet.
In MVC-speak these are our controllers, but in a very low-tech way. Here
we deal with user input and maybe return some JSON. Or render a template file.</p>

<h2>Setting up autoloading</h2>

<p>Composer automatically knows how to pull in external dependencies and make
them available to our application. It does however not know what classes it should
load from our own code. We need to configure that. To change that, we insert
the following into our <code>composer.json</code></p>

<pre><code class="language-json">"autoload": {
    "files": [ "src/helpers.php" ],
    "psr-4": {
        "Pretzel\\": "src/Pretzel"
    }
}
</code></pre>

<p>The first key, <code>files</code>, tells Composer specific files to load. This is useful
if you have files that don't contain classes. And our <code>helpers.php</code> is just a
collection of plain old functions.</p>

<p>The <code>psr-4</code> key tells Composer which namespace to look for and where to look
for it. Because I am always on-brand, I called my namespace <code>Pretzel</code>. Feel
free to change this. We also tell Composer to look in the <code>src/Pretzel</code> directory
for any classes it can find.</p>

<p>And since I always have trouble remembering this: It's <strong>namespace</strong> first, then
the <strong>directory</strong>. The namespace has to end in an escaped backslash.</p>

<p>After you've made the changes, don't forget to run <code>composer dump-autoload</code> so the
autoloader is aware of the new configuration.</p>

<h2>Setting up our application with <code>bootstrap.php</code></h2>

<p>Whenever our application runs, there are certain things we want to do.
We always want to load our <code>.env</code> file. We always want to load the Composer
dependencies. If we have one, we always want to set up our library for
database connections.</p>

<p>This all happens in <code>bootstrap.php</code>. For our basic project, all we need is 3 lines.</p>

<pre><code class="language-php">// bootstrap.php

&lt;?php

require __DIR__ . '/vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable([__DIR__]);
$dotenv-&gt;load();
</code></pre>

<p>The first <code>require</code> is a common sight for anyone who has dealt with Composer before.
It loads all dependencies into our application.</p>

<p>Line 2 and 3 tell the <code>vlucas/phpdotenv</code> package to look in the current directory
for a <code>.env</code> file and load it. If we don't have an <code>.env</code> file our application will
yell at us about that and stop execution.</p>

<h2>Adding the first helper</h2>

<p>Now that we have set up the loading of our <code>.env</code> file, we have all the values
contained in that file loaded into the <code>$_ENV</code> <a href="https://www.php.net/manual/en/language.variables.superglobals.php">superglobal</a>.</p>

<p>That's neat, but interacting with superglobals directly is generally considered
a Bad Idea™. So instead, let's wrap our <code>$_ENV</code> variable into a nice <code>env()</code> function.</p>

<pre><code class="language-php">// src/helpers.php

&lt;?php

if (!function_exists('env')) {
    function env($value, $default = null) {
        return $_ENV[$value] ?? $default;
    }
}
</code></pre>

<p>We have added the <code>function_exists</code> check, because some packages (e.g. <code>illuminate/support</code>)
might bring along their own version of an <code>env()</code> function. If that's the case 
we don't want to overwrite it and just use the one they give us instead.</p>

<p>Inside the function we check if <code>$_ENV</code> holds the value we want. If it does not
we use the null-coalescing operator <code>??</code> to get a default value. If there's no
default value, we just return an empty value.</p>

<h2>Making our application shareable</h2>

<p>Often times when building projects, it can be useful to have a public endpoint that
points to your local development environment. For example, when you're working
with a third-party service that uses webhooks to notify you of events.</p>

<p>Thus it makes sense to make the application easily shareable.</p>

<p>For this, we need one more package. It's called <code>expose</code> and it's made by the cool
people at BeyondCode. You can learn more about it <a href="https://beyondco.de/docs/expose/introduction">on their website</a>.
Let's install it!</p>

<pre><code class="language-bash">$ composer global require beyondcode/expose
</code></pre>

<div class="remark">
<p>
Note that I'm installing it globally. You don't have to. It's just convenient.
</p>
</div>

<p>But to share an application we need to have it be served by some kind of HTTP
server. Many tutorials would go on a long tangent about setting up nginx or Apache
here. For our little framework here, however, the built-in PHP server is plenty.</p>

<p>You call it like this:</p>

<pre><code class="language-bash">$ php -S localhost:8080 -t public
</code></pre>

<p>Here the port can be anything you want. The <code>-t</code> option allows us to specify which
directory is the public root of our application. In our case that's <code>public/</code>.</p>

<p>After running this you can open up your browser and look at <code>localhost:8080</code> to
see the result. Since we only have an empty directory at this point, you'll get
a 404 error.</p>

<p>Now let's combine <code>expose</code> and the PHP server into one thing:</p>

<pre><code class="language-bash">php -S localhost:8080 -t public &amp;&gt;/dev/null &amp; expose share localhost:8080
</code></pre>

<div class="remark">
<p>
There are some UNIX-y things happening here. Windows users may want to use WSL.
</p>
</div>

<p>This command first starts up the server and redirects all output to <code>/dev/null</code>.
That means we're essentially throwing it away. The PHP server just logs whatever
requests are coming in anyways. Expose does the same thing, so we don't need that
output.</p>

<p>The <code>&amp;</code> after the first command tells the PHP server to continue running in the
background. This allows us to immediately run another command, which is <code>expose</code>!</p>

<p>We point it to serve whatever is happening at <code>localhost:8080</code>, which is where
our application currently lives!</p>

<p>And since all that is a bit of a hassle to type, we can wrap it in a so-called
Composer script. To do so, add the following to your <code>composer.json</code></p>

<pre><code class="language-json">"scripts": {
    "dev": "php -S localhost:8080 -t public &amp;&gt;/dev/null &amp; expose share localhost:8080"
}
</code></pre>

<p>You can rename <code>dev</code> to whatever makes the most sense to you. And now that we've
added this, we can run</p>

<pre><code class="language-bash">$ composer run dev
</code></pre>

<p>and immediately have a publically shareable link to our app. Neat!</p>

<h2>Using the framework</h2>

<p>Now that everything is set up, we can use this for a very contrived example of
building an application. It'll just take a value from our <code>.env</code> file and output
it with some extra text we generate in a class.</p>

<p>To get started, let's add a value to our <code>.env</code></p>

<pre><code class="language-toml">MY_BIGGEST_SECRET="I love puppies and kittens"
</code></pre>

<div class="remark">
<p>
Shocking, I know.
</p>
</div>

<p>Next, let's create a <code>PretzelFactory.php</code> file in our <code>src/</code> directory and add
the following.</p>

<pre><code class="language-php">// src/PretzelFactory.php

class PretzelFactory
{
    public static function getPretzel()
    {
        return '🥨';
    }
}
</code></pre>

<p>This is not a factory in the true sense of the word, but it gives us an infinite
supply of pretzels and that counts for something!</p>

<p>To wrap it up we create an <code>index.php</code> file in our <code>public</code> directory.</p>

<pre><code class="language-php">// public/index.php

// Our setup file
require __DIR__ . '/../bootstrap.php'; 

use Pretzel\PretzelFactory;

echo sprintf(
    '%s %s'
    PretzelFactory::getPretzel(),
    env('MY_BIGGEST_SECRET')
);
</code></pre>

<p>Here we read load in our <code>bootstrap.php</code> to get the app going. Then we load
in the <code>PretzelFactory</code> class. Finally we output a formatted string containing
a pretzel and our big secret.</p>

<p>And now we can share it like this</p>

<pre><code class="language-bash">$ composer run dev
</code></pre>

<p>The result should look a little bit like this</p>

<p><x-image
    src="expose.png"
    alt="A web page showing the text: 🥨 I love puppies and kittens"
/></p>

<h2>Closing thoughts</h2>

<p>This concludes the basic project I will be using for my future blog posts. Anyone
who wants to have a pre-made copy of it can clone it <a href="https://github.com/pretzelhands/base">from GitHub</a>.</p>

<p>In the future when starting from this point I will be linking this blog post and the repository,
instead of explaining the entire process every time.</p>

<p>Unless of course we diverge from it. Then there will be a proper explanation.</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Mon, 25 Jan 2021 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Build a Telegram bot in PHP]]></title>
        <link>https://pretzelhands.com/posts/build-a-telegram-bot-in-php</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/build-a-telegram-bot-in-php</guid>
        <description><![CDATA[Learn how to set up your own Telegram bot, respond to commands and handle arguments. A quick tour of the Telegram Bot API!]]></description>
        <content:encoded><![CDATA[<p>Telegram is one of the two cool kids on the messaging app market right now. Especially since the
recent upset with WhatsApps new data sharing policy the user numbers have positively skyrocketed.
There's now over 500 million people using the app.</p>

<p>Personally I've been using it since 2015. Since 2018, I've used it for 95% of my daily communication.
During that time I also found out that Telegram has a really cool Chat Bot API! So I've built a few.</p>

<ul>
<li>One keeps track of my appointments and sends reminders</li>
<li>One tracks shipping goals for a chat group of my friends</li>
<li>One that checked who in the group got made fun of the most</li>
<li>One that tracks some metrics of my company (audience growth, ...)</li>
</ul>

<p>Today, I'd like to teach you how to do the same. Because a future where we all have a personal
army of automated chat robots is definitely one I want to live in!</p>

<h2>Prerequisites</h2>

<p>To follow along with this article you'll need only two things.</p>

<ul>
<li>A working installation of PHP 7.2+ and <a href="https://getcomposer.php">Composer</a></li>
<li>The <a href="https://telegram.org">Telegram app</a> and an account</li>
</ul>

<p>To create our bot, we need a special token from Telegram itself. We can get this
from an automated bot, too! It's called <a href="https://t.me/botfather">@BotFather</a> and
it will guide you through the setup process. So let's do that now!</p>

<p><x-image
    src="telegram-bot/01.png"
    caption="BotFather is quite the wordy chap."
    alt="Setup process with BotFather"
/></p>

<p>Congratulations! You have set up your first Telegram bot. Be sure to note down that token from
the last message. We'll need it in a few minutes. You can also further customize your bot with
a profile image and such. I'll leave that up to you.</p>

<h2>Setting up your PHP project</h2>

<p>Now it's time to get down to the actual core of this project: Implementing the chat bot.
We'll start as with any good PHP project in <code>$CURRENT_YEAR</code>: By setting up a Composer project.</p>

<pre><code class="language-bash">$ composer init
</code></pre>

<p>While we could directly deal with the HTTP API of Telegram, there is a <a href="https://github.com/irazasyed/telegram-bot-sdk">very nice library</a> 
already built for us by Irfaq Sayed. So let's grab that too!</p>

<pre><code class="language-bash">$ composer require irazasyed/telegram-bot-sdk
</code></pre>

<div class="remark">
<p>For any Laravel fans: This library also provides a facade. Super neat!</p>
</div>

<p>Then it's time to define our directory structure. When I build Telegram bots, I generally follow 
a structure that looks like this:</p>

<pre><code class="language-xml">chat-bot/
├─ composer.json
├─ composer.lock
├─ vendor/
├─ src/
├─ public/
├─ .env
├─ bootstrap.php
├─ helpers.php
├─ setup.php
</code></pre>

<p>First we have our regular files as generated by Composer.</p>

<p><code>src/</code> is the folder where we place all of our commands and other application-specific code.</p>

<p><code>public/</code> will contain the code we expose to the scary, scary reality of the internet.</p>

<p><code>bootstrap.php</code> will set up some things such as including our autoloader 
and loading variables from our <code>.env</code> file. We'll include that one a lot.</p>

<p><code>helpers.php</code> usually contains small utility functions that don't fit anywhere else.</p>

<p>Last but not least, <code>setup.php</code> is a small file that helps us set up our connection with Telegram.</p>

<p>Now would be a good time to copy that token we got from BotFather earlier and put it in our <code>.env</code> file.</p>

<pre><code class="language-toml"># .env
TELEGRAM_BOT_TOKEN="1234567890:YOUR_TOKEN"
</code></pre>

<div class="remark">
<p>You can name it whatever you like. My chosen name is just the default of the library.</p>
</div>

<p>To wrap up this setup sequence all that's left to is to set up our autoloader. To do this we modify our
<code>composer.json</code> and add this:</p>

<pre><code class="language-json">"autoload": {
    "psr-4": {
        "Pretzel\\": "src/"
    },
    "files": [ "helpers.php" ]
}
</code></pre>

<div class="remark">
<p>Again, the namespace can be whatever you prefer. Doesn't have to be self-centered.</p>
</div>

<p>The change essentially summarizes to this: Load everything from <code>src/</code> into the <code>Pretzel</code> namespace,
and please also load the <code>helpers.php</code> file specifically.</p>

<p>Now we finally have our PHP project ready to go!</p>

<h2>Setting up the connection with Telegram</h2>

<p>Telegram works with an event-based system to let you know when new updates arrive. For this purpose
we need to set up a webhook handler. In real world terms this just a PHP file that Telegram sends
a request to every time somebody messages our bot. Fancy!</p>

<p>First, we'll need our bot's token. That is how we identify to Telegram who we are. As you may remember
we put that into a <code>.env</code> file. So let's set up loading variables from that.</p>

<pre><code class="language-bash">$ composer require vlucas/phpdotenv
</code></pre>

<p>Then we add the following to our <code>bootstrap.php</code></p>

<pre><code class="language-php">// bootstrap.php

&lt;?php

require __DIR__ . "/vendor/autoload.php";

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv-&gt;load();
</code></pre>

<p>Thanks to that short and sweet snippet we can now easily grab values from our <code>.env</code> file by using the
<code>env()</code> function. This is not a native PHP function, but the Telegram Bot SDK brings it along as a dependency.</p>

<p>Next, I want to set up a small helper function in our <code>helpers.php</code></p>

<pre><code class="language-php">// helpers.php

&lt;?php

function telegram(): \Telegram\Bot\Api
{
    return new \Telegram\Bot\Api(
        env('TELEGRAM_BOT_TOKEN')
    );
}
</code></pre>

<p>This is purely for convenience. It allows us to grab an instance of the Telegram API wrapper without
constantly having to query the <code>.env</code> file again. Now we can combine those two things in our <code>setup.php</code> file.</p>

<pre><code class="language-php">// setup.php

&lt;?php

require 'bootstrap.php';

telegram()-&gt;setWebhook(['url' =&gt; env('TELEGRAM_WEBHOOK')]);

echo "Setup the Telegram webhook!";
</code></pre>

<p>Stuff like this is why the <code>telegram()</code> helper is useful</p>

<p>Hold your horses. Readers with sharp eyes will have noticed, that I'm getting a <code>TELEGRAM_WEBHOOK</code>
from the <code>.env</code> file. We don't have that yet, right? Right.</p>

<p>We need to pass Telegram some kind of URL it can send its updates to. How do we do this on localhost?
I'm glad you asked. Being a PHP developer in 2021 is a wonderful experience, so we have the perfect tool for
this. It's called <code>expose</code> and it's made by BeyondCode.</p>

<p><x-disclaimer>
    <strong>Note:</strong> There are other options out there too. Something like ngrok would work just as well. I like
    expose because it's easy to set up and provides custom subdomains for free.
</x-disclaimer></p>

<h3>Setting up your local webhook</h3>

<p>To get started with getting an URL for our local environment, first we install <code>expose</code>.</p>

<pre><code class="language-bash">$ composer global require beyondcode/expose
</code></pre>

<p>This installs the <code>expose</code> utility globally and gives us access to it. If you've been
using Composer for a while you've probably already done that, but make sure that <code>composer global config bin-dir --absolute</code>
is in your <code>$PATH</code>.</p>

<p>Next we need to run our PHP somehow. For a project such as this the integrated PHP web server is fine.
So we can use the following in the root directory of your project to get it running:</p>

<pre><code class="language-bash">$ php -S localhost:8080 -t public

[Mon Jan 18 11:28:18 2021] PHP 8.0.0 Development Server (http://localhost:8080) started
</code></pre>

<p>Then, open another terminal tab and run the following:</p>

<pre><code class="language-bash">$ expose share localhost:8080 --subdomain=pbbot
</code></pre>

<div class="remark">
<p>The subdomain option can be any of your choice. You can even name it "hunky-dory"</p>
</div>

<p>In the resulting output towards the end you should find a URL ending in <code>sharedwithexpose.com</code>.
In my case it is <code>pbbot.sharedwithexpose.com</code> and that is the URL we need. <strong>Remember it!</strong></p>

<h3>Making it more ergonomic (optional)</h3>

<p>Running two different terminal tabs at once is cumbersome. So let's make use of ✨magical Composer scripts✨
so we only have to run one command to get our bot up to speed. We can make use of the <code>&amp;</code> functionality of 
the shell to run both things at the same time. So you can add this to your <code>composer.json</code></p>

<pre><code class="language-json">"scripts": {
    "dev": "php -S localhost:8080 -t public &amp;&gt;/dev/null &amp; expose share localhost:8080 --subdomain=pbbot"
}
</code></pre>

<div class="remark">
<p>Adjust the subdomain and port to your liking.</p>
</div>

<p>The <code>&amp;&gt;/dev/null</code> stuff just ensures that the output of the original PHP web server does not mess with
<code>expose</code>. Since <code>expose</code> gives us a nice request overview, we don't need it anyways. Now you can run
a single command, and your bot comes alive!</p>

<pre><code class="language-bash">$ composer run dev
</code></pre>

<p>Now we can add our webhook URL to our <code>.env</code> file</p>

<pre><code class="language-toml"># .env

TELEGRAM_BOT_TOKEN="1234567890:YOUR_TOKEN"
TELEGRAM_WEBHOOK="https://pbbot.sharedwithexpose.com/webhook.php"
</code></pre>

<div class="remark">
<p>
Note that the URL *must* be HTTPS. Security and all that.
</p>
</div>

<p><code>webhook.php</code> will be the file we use to handle any incoming requests from Telegram. We'll create it in
the next step. First, we want to run our little <code>setup.php</code> script.</p>

<pre><code class="language-bash">$ php setup.php
Setup the Telegram webhook!
</code></pre>

<div class="remark">
<p>
Great success!
</p>
</div>

<h3>Setting up your webhook handler</h3>

<p>So we just told Telegram to please send us any information to <code>webhook.php</code>, but we don't have that file yet.
We'll just create it in our <code>public/</code> folder. The webhook handler is essentially responsible for grabbing
any unprocessed updates, forwarding them to our bot, and returning a result to Telegram. It looks like this:</p>

<pre><code class="language-php">// public/webhook.php

&lt;?php

require __DIR__ . '/../bootstrap.php';

telegram()-&gt;commandsHandler(true);
</code></pre>

<p>I think by now you can see why the Telegram Bot SDK is useful. The fact that we have to call only one function
to set up an entire command system is super useful and keeps productivity high!</p>

<h2>Writing your first command</h2>

<p>Now that we have all the annoying setup stuff out of the way, we can write our first command.
Let's start with the most bare-bones command imaginable: When we say <code>/hello</code> to our bot, we want it to
say <code>Hello World</code> back to us.</p>

<p>First, we have to create a new class for our command. I'll put it in <code>src/Commands/HelloCommand.php</code>, so
we can access it through the class <code>Pretzel\Commands\HelloCommand</code>. Every command we create needs to inherit
from the Bot SDKs base class. It also needs to have a name. So the basic structure looks like this.</p>

<pre><code class="language-php">// src/Commands/HelloCommand.php

&lt;?php

namespace Pretzel\Commands;

use Telegram\Bot\Commands\Command;

class HelloCommand extends Command
{
    protected $name = 'hello';

    public function handle()
    {
        // TODO: Implement command
    }
}
</code></pre>

<p>Now inside the <code>handle()</code> function, we have access to a whole bunch of <code>replyWith*</code> type functions
that we can use to respond to the message we just received. The easiest way is to use <code>replyWithMessage</code>.</p>

<p><x-image
    src="telegram-bot/02.png"
    alt="Suggestion box showing all replyWith* functions"
/></p>

<p>These functions all take a bunch of arguments in the form of an array. I can't even tell you
all of them by heart. Thankfully, there's good documentation! To find out what you need to send
to the function, you can just look it up <a href="https://telegram-bot-sdk.readme.io/reference#sendmessage">in the API Methods documentation</a>.
I also highly recommend the <a href="https://core.telegram.org/bots/api#sendmessage">official Telegram documentation</a> for more
detailed information.</p>

<p>Each <code>send*</code> function corresponds to one of our <code>replyWith*</code> functions.</p>

<p>The important thing that these <code>replyWith*</code> functions do is adding the <code>chat_id</code> parameter to our arguments.
So we can safely ignore that one. The other parameter we need is <code>text</code>. It should contain, as the name implies,
the text of our message.</p>

<p>So now our class should look something like this</p>

<pre><code class="language-php">// src/Commands/HelloCommand.php

&lt;?php

namespace Pretzel\Commands;

use Telegram\Bot\Commands\Command;

class HelloCommand extends Command
{
    protected $name = 'hello';

    public function handle()
    {
        $this-&gt;replyWithMessage([
            'text' =&gt; 'Hello World!'
        ]);
    }
}
</code></pre>

<div class="remark">
<p>This would be an amazing use case for named arguments from PHP 8!</p>
</div>

<h3>Registering it with the bot</h3>

<p>Now we need to register this command with the bot, so it actually knows what commands are available.
To do this, we go back to our <code>bootstrap.php</code> file and add the following</p>

<pre><code class="language-php">// bootstrap.php

telegram()-&gt;addCommands([
    Pretzel\Commands\HelloCommand::class,
]);
</code></pre>

<div class="remark">
<p>The namespace of your command is of course different</p>
</div>

<p>And now that we have done this, we can interact with our bot! Open up the chat with your bot in Telegram
and try sending it a <code>/hello</code> command. It should respond to you right away!</p>

<p><x-image
    src="telegram-bot/03.png"
    caption="Look at the little sunshine!"
    alt="Bot saying 'Hello World'"
/></p>

<p><x-newsletter /></p>

<h3>Working with arguments</h3>

<p>Of course, commands are much more interesting when you're able to send arguments to them. Say we want to
greet a friend! How do we pass in their name?</p>

<p>For this, the <code>Command</code> class we inherit from offers a <code>$pattern</code> property. Here we can either use
named arguments in braces like <code>{this}</code>, or we can pass in a custom regex.</p>

<pre><code class="language-php">// src/Commands/HelloCommand.php

class HelloCommand extends Command
{
    protected $pattern = '{name}'; // Our argument is called 'name'
    protected $pattern = '{name}?'; // We have an optional argument called 'name'
    protected $pattern = '.+' // We'll take in any argument with 1 or more characters. It'll be called 'custom'

    // .. SNIP ..
}
</code></pre>

<p>So let's say we want our bot to greet a person by their name if it receives one. Else we just greet the world,
because everyone should be greeted once in a while. For this we set our <code>$pattern</code> to <code>{name}?</code>.</p>

<p>Then, in our <code>handle()</code> function we can just grab the pre-supplied <code>$arguments</code> property:</p>

<pre><code class="language-php">// src/Commands/HelloCommand.php

&lt;?php

class HelloCommand extends Command
{
    protected $name = 'hello';
    protected $pattern = '{name}?';

    public function handle()
    {
        $arguments = collect($this-&gt;arguments);

        // .. SNIP ..
    }
}
</code></pre>

<p>As you can see, I used a the <code>collect</code> function when getting the arguments. This is another beautiful thing
from the Laravel world and brought along by the Bot SDK. Collections are basically super-powered PHP arrays
with a much cleaner way of working with them.</p>

<p>Of course you can also just handle the <code>$arguments</code> array yourself, but I vastly prefer this approach.</p>

<p><x-disclaimer>
    To learn more about Laravel collections, you can read the <a href="https://laravel.com/docs/collections">excellent documentation</a>.
    They're available as a standalone package and are a massive quality-of-life improvement.
</x-disclaimer></p>

<p>Now we can grab the <code>name</code> key from our arguments, or return a default value if it's not there. Then
we insert this argument into a string and send it back to the user. That looks like this</p>

<pre><code class="language-php">// src/Commands/HelloCommand.php

&lt;?php

class HelloCommand extends Command
{
    protected $name = 'hello';
    protected $pattern = '{name}?';

    public function handle()
    {
        $arguments = collect($this-&gt;arguments);

        $this-&gt;replyWithMessage([
            'text' =&gt; sprintf(
                'Hello %s!',
                $arguments-&gt;get('name', 'World')
            )
        ]);
    }
}
</code></pre>

<p>After saving our command we can try it immediately! Go over to your bot and send it <code>/hello &lt;YOUR_NAME&gt;</code>
and watch as it greets you with it's mechanically engineered happiness!</p>

<p><x-image
    src="telegram-bot/04.png"
    caption="Machines are so cute"
    alt="Bot greeting user by name"
/></p>

<h2>Sending unprompted messages</h2>

<p>Of course bots only have limited use if they only reply to you when you ask for them. Luckily, we can also
send messages without any prior command. Just because we feel like it. For this, we need the <code>chat_id</code> of
the person we want to send the messages to, or the <code>@username</code> of the group we want to send messages to.</p>

<p>To find out your personal <code>chat_id</code> you can just contact <a href="https://t.me/RawDataBot">@RawDataBot</a>, who will
tell you in the first message.</p>

<p><x-image
    src="telegram-bot/05.png"
    caption="You want the ID field I so helpfully redacted"
    alt="Raw Data Bot showing information about a private message"
/></p>

<p>If you have a private group without a <code>@username</code>, you can also just add @RawDataBot to the group to find the
<code>chat_id</code> of the group. It will be a negative number.</p>

<p><x-image
    src="telegram-bot/05.png"
    caption="Same field, different contents"
    alt="Raw Data Bot showing information about a group message"
/></p>

<p>And then you can use the <code>sendMessage</code> function of the Telegram API object. As demonstration, let's add an
unprompted message to our <code>setup.php</code> that we successfully set up our webhook.</p>

<pre><code class="language-php">// setup.php

&lt;?php

require 'bootstrap.php';

$telegram = telegram();
$telegram-&gt;setWebhook(['url' =&gt; env('TELEGRAM_WEBHOOK')]);
$telegram-&gt;sendMessage([
    'chat_id' =&gt; env('PRETZELHANDS_CHAT_ID'),
    'text' =&gt; 'Successfully set up webhook'
]);
</code></pre>

<p>That was pretty easy, wasn't it?</p>

<h2>Processing raw messages without commands</h2>

<p>For some bots, we may want to process messages that are sent without command, just using the raw information
that Telegram provides us. This is once again, relatively easy. After passing through the commands system,
the SDK gives us an update object in the webhook handler. We can then use that to do whatever!</p>

<pre><code class="language-php">// public/webhook.php

&lt;?php

&lt;?php

require __DIR__ . '/../bootstrap.php';

$updates = telegram()-&gt;commandsHandler(true);

// Telegram\Bot\Objects\Update Object(
//     [items:protected] =&gt; Array(
//         [update_id] =&gt; 290647531
//         [message] =&gt; Array(
//             [message_id] =&gt; 81
//                 [from] =&gt; Array(
//                 [id] =&gt; 0000000000
//                 [is_bot] =&gt;
//                 [first_name] =&gt; Richard
//                 [last_name] =&gt; Blechinger
//                 [username] =&gt; pretzelhands
//                 [language_code] =&gt; en
//             )
// 
//             [chat] =&gt; Array(
//                 [id] =&gt; 00000000000
//                 [first_name] =&gt; Richard
//                 [last_name] =&gt; Blechinger
//                 [username] =&gt; pretzelhands
//                 [type] =&gt; private
//             )
// 
//             [date] =&gt; 1610977759
//             [text] =&gt; abc
//         )
//     )
// )

</code></pre>

<p>All properties on this update are directly accessible. So you can use <code>$update-&gt;message-&gt;text</code> to grab
the relevant messages text etc.</p>

<p>This is the process I used in another bot to parse date and event information directly from a users message. 
Aside from the fact that parsing natural language dates is painful, it works great!</p>

<h2>Deployment</h2>

<p>Deployment for this setup should be straightforward for anyone who has ever dealt with other PHP deployments before.
Doing so is left as an exercise for the reader, but it boils down to three points</p>

<ul>
<li>Push your files onto your server</li>
<li>Point your webserver to serve files from <code>public/</code></li>
<li>Update webhook URL to <code>yourdomain.com/webhook.php</code> using <code>setup.php</code></li>
</ul>

<p>After that you're good to go!</p>

<h2>Closing thoughts</h2>

<p>This article should have given you enough of an overview to get started with writing your own
Telegram bots for whatever purposes. From here on out, the possibilities are mostly limited to
what you can imagine.</p>

<p>My main bot currently tracks newsletter growth, Twitter growth, and during Notebag's lifecycle it
tracked the daily amount of money earned. It's like a little daily dashboard informing me how my
side-project life is going. The second bot makes sure I show up to any appointments I have on time.</p>

<p>The code for this project is also <a href="https://github.com/pretzelhands/basic-telegram-bot">available on GitHub</a></p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Tue, 19 Jan 2021 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Restarting the blog]]></title>
        <link>https://pretzelhands.com/posts/restarting-the-blog</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/restarting-the-blog</guid>
        <description><![CDATA[Returning to proper blogging after two years of it laying dormant. What could possibly go wrong?]]></description>
        <content:encoded><![CDATA[<p><x-disclaimer>
    <strong>TL;DR: New blog, who dis? Please help me decide what to write about by <a href="https://twitter.com/pretzelhds/status/1348938591431487488">voting in this Twitter poll</a>. Thanks.</strong>
</x-disclaimer></p>

<p>Test, test.. 1, 2. Is this thing on? Wonderful! Welcome back, everybody. Somewhere
back in 2019 I had a month where I blogged a lot. I wrote most of the articles you see
on here and many of them are actually quite popular! There's more than a thousand people 
stopping by here every month to learn about <a href="/posts/command-line-flags">shell scripting</a> from me.</p>

<p>I find that very silly, but hey. Glad that I can help some of you with your questions.
And I'd like to do that again.</p>

<p>In the past two years I've learned a lot about programming.
There's so many things I still haven't shared about payments and e-commerce that I learned
while working for Wirecard.</p>

<p>In 2021 I have taken a conscious decision to step away from taking on more freelance work than
I currently have. Instead I want to focus more on my own projects again. To build things that 
are interesting, helpful, or just funny to me. And part of that is engaging with people through this blog.</p>

<h2>What's new?</h2>

<p>Since its <a href="/posts/breaking-the-rules">humble beginnings</a>, this blog has gone through three major
revisions. The third one being the current one. Here's a short overview:</p>

<ul>
<li>Two-file PHP setup that rendered Markdown on demand</li>
<li>Custom PHP setup that rendered Markdown and Mustache</li>
<li>Custom PHP static site generator from Markdown and Mustache</li>
</ul>

<p>I didn't <em>intend</em> to build a static site generator, but the pieces were all just lying there
and so I rewired them. Along with making this site <em>super</em> fast, I also added these things:</p>

<ul>
<li>Dedicated <a href="/series">post series</a> for multi-part things</li>
<li>A <a href="/newslettter">weekly newsletter</a> you can subscribe to</li>
<li>After many requests: an <a href="/feed">RSS feed</a> for you to consume</li>
<li>Easier sharing to various places</li>
<li>An actual navigation</li>
</ul>

<p>And then I reworked how posts look and feel a little bit, which gives me the ability
to write more structured and informative content.</p>

<p><x-disclaimer>
For example I can now use this box to give more detailed context-specific information about various topics. Or snarky side comments.</p>

<p>Neat.
</x-disclaimer></p>

<h2>What's next?</h2>

<p>With this announcement, the blog is officially considered "re-launched" by me and while I have a few topics in mind,
I'd love to hear what you want to see me write about! Because it's more fun to write about stuff that is actually
interesting to people.</p>

<p>If you want to help decide, please <a href="https://twitter.com/pretzelhds/status/1348938591431487488">vote in this Twitter poll</a> and I'll get on it for next week's post! Until then,
stay safe, stay healthy and have a good time!</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Tue, 12 Jan 2021 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Au Revoir: The End of Notebag]]></title>
        <link>https://pretzelhands.com/posts/end-of-notebag</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/end-of-notebag</guid>
        <description><![CDATA[Giving insights and reasons as to why I put Notebag to rest and what is coming from here on out]]></description>
        <content:encoded><![CDATA[<p><strong>TL;DR: I am stopping work on Notebag.</strong> It is causing me lots of stress and guilt and I currently
have neither the capacity nor the will to update it right now and for the foreseeable future. 
It is available free and open-source <a href="https://github.com/pretzelhands/notebag">on GitHub.</a> You can extend it!
If you bought your license in the past 30 days, send me an email with your order number and I'll refund it.</p>

<hr />

<p>So. Here we are. The irony of my last two blog posts being about the start and end of something is not lost
on me. I know that a few of you are probably confused, angry and perhaps even a bit sad right now, and I
apologize in advance. And some critical voices that have been haunting me around the internet have finally
been proven right.</p>

<p>Now let me tell you why exactly I am stopping work on Notebag, what will happen to it and where I will
be going from here.</p>

<h2>The Why</h2>

<p>One or two of you reading this might perhaps know what it's like when you start a new project. You come
up with a concept! Ideas burst through your head! You are itching to tear the world apart and make
things come alive exactly the way you imagined them. This is an absolute high of productivity and you
hack away until late at night, only to get up early next morning and get right back into it.</p>

<p>That was me.</p>

<p>That's how Notebag started, doubly fueled by the fact that the world in 2020 is .. something else
and I was shit-out-of-luck in terms of freelancing contracts. So I clung to the one thing I had and put
all of my energy into it and I worked to make it into something I thought was nice.</p>

<p>I pushed and pushed each day. Bigger! Better! More features!</p>

<p>And this worked well for a while, until the inevitable tiredness came creeping in. So I put Notebag on the
backburner. New freelancing contracts finally started trickling in and I found myself busy with two kinds of
work: The one paying my bills and the one I did on Notebag.</p>

<p>It was a struggle. Every day after work I tried to pour some of my remaining energy into Notebag, but nothing
happened. I failed to implement the most basic things and bit by bit, this intense feeling of guilt set in.</p>

<p>In time, I started avoiding Twitter. Because everytime I tweeted, I felt like someone out there would be
judging me. Why wasn't I reporting progress on Notebag? What was I even up to? And so I withdrew to the confines
of Telegram and ignored Twitter. DMs came in asking and I was too scared to reply.</p>

<p>Worst of all, I was and am building this subconscious pressure on myself that I am not really allowed to work on anything
else. Why would I work on <code>Project X</code> when Notebag fans are clamoring for updates? That makes me a bad person, no? This
has led me to a state where I was afraid to program outside of my contracts. Not a great spot to be in.</p>

<p>When you are not happy with things, you should change them. I am not happy with how Notebag is currently holding
a soft stranglehold on my life and so I am changing it.</p>

<h2>The What</h2>

<p>What does all of this mean for Notebag's future? First of all, I have <a href="https://github.com/pretzelhands/notebag">published the source code.</a>
It's MIT-licensed and on GitHub, free for everyone to download, build and play with. In my wildest dreams of course there
would be a shining community hero that rises up and picks up where I left off. But that is unlikely to happen.</p>

<p>So at least as a consolation, Notebag in its entirety is available for everyone, for all time, for free. If someone is
seriously willing to make pull requests and improve the code, I am happy to release signed builds with those changes
included. If anyone has specific questions about the architecture and why my code is so bad, I will gladly answer them.</p>

<p>This is, I think, the most elegant solution. It at least guarantees that the code doesn't just gather dust on my machine,
forever unreachable for any interested person.</p>

<p>And since there is still the occasional sale of a license, I offer anybody who bought their license in the past 30 days
(after September 27) a full refund, no questions asked. To avail this, just <a href="&#109;&#x61;&#105;&#x6c;&#x74;&#111;&#x3a;&#104;&#x65;&#x6c;&#108;&#x6f;&#64;&#x70;&#x72;&#101;&#x74;&#122;&#x65;&#x6c;&#104;&#x61;&#110;&#x64;&#x73;&#46;&#x63;&#111;&#x6d;&#x3f;&#115;&#x75;&#98;&#x6a;&#x65;&#99;&#x74;&#61;&#x4e;&#x6f;&#116;&#x65;&#98;&#x61;&#x67;&#37;&#x32;&#48;&#x52;&#x65;&#116;&#x69;&#114;&#x65;&#x6d;&#101;&#x6e;&#116;&#x25;&#x32;&#48;&#x52;&#101;&#x66;&#x75;&#110;&#x64;">send me an email</a>
or send me a Twitter DM.</p>

<h2>The Future</h2>

<p>Notebags future probably does not hold many further improvements. I still use it as my personal preferred
note-taking app, because that was its purpose. It works for me and my workflow and I hope that other people will keep 
on using it or maybe learn a thing or two from the source code.</p>

<p>Personally I only have one or two side projects, but I'll probably put them on indefinite hold. Clean slate. 
There's a few  other hobbies I've been meaning to get around to and I think right now is a good time to do that. And 
perhaps, somewhere down the line an idea will strike me and I'll give in to the itch to build again. But the time for 
that is not right now.</p>

<h2>Final words</h2>

<p>I would again like to apologize to anyone who put high hopes into this app. Turns out that the detractors who pointed out
my legacy of abandoned apps were onto something! It sucks to be known for this, but maybe one day that will change. Or
maybe it won't.</p>

<p>As it is, Notebag will keep working in its usual state for the foreseeable future. But maybe <code>Au revoir</code>, doesn't have
to mean goodbye forever. Not that I have any hopes of picking it back up, but if 2020 has taught the world anything, 
it's that life is unpredictable.</p>

<p>For my final point I'd like to give a special shout-out to <a href="https://twitter.com/Clo__S">@Clo__S</a>, who aside from being
an early adopter and wonderful source of feedback was probably Notebag's most vocal fan. Thanks for all the support along
the way.</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Tue, 27 Oct 2020 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Scratching my own itch: Building Notebag]]></title>
        <link>https://pretzelhands.com/posts/scratching-the-itch</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/scratching-the-itch</guid>
        <description><![CDATA[The story of my annoyance with other note taking tools and finally pulling the trigger to scratch my own itch.]]></description>
        <content:encoded><![CDATA[<p><strong>Notebag is on <a href="https://producthunt.com/posts/notebag-2">Product Hunt</a>! Go check it out!</strong></p>

<p>I was never any good at coming up with ideas. I'm not sure whether you could attribute it to a lack of creativity, my life
being "boring" by many peoples standards or whether I've just been satisfied with existing solutions to all of my discomforts
and problems.</p>

<p>Well, except for that one thing where I never found a satisfying solution: Note taking. I tried a few different approaches.
For a while I would put my notes into my IDEs scratch file feature. That wasn't exactly a very scalable or searchable
solution, but it was good enough for storing code snippets in.</p>

<p>For another while I had my writings in Apple Notes. I liked the simplicity of it, but it didn't have support for typing
out notes in Markdown, which made formatting cumbersome.</p>

<p>Then I tried note taking wunderkind Bear, which is beloved by many. And for a while I was very happy with it. It has a wonderful
set of features and <em>great</em> user experience. Seriously, hats off to the people at Bear. But I still had to reach for my
mouse to do things every now and then. And there was still the thing where I had to either open the app on demand or switch
around all my open windows with <code>Cmd-Tab</code>. It still felt off.</p>

<p>This entire journey went on for a good year or so. I had fought off the idea of writing a note taking app because I felt
that:</p>

<ul>
<li>A) It would be super complicated</li>
<li>B) The market for note taking apps is incredibly over saturated</li>
</ul>

<p>I still believe that the second point holds true to a certain extent. There is certainly a lot of competition out there.
But the first point was shattered when I found out about a lovely little thing called <a href="https://tiptap.scrumpy.io/">tiptap</a>.
It's based on ProseMirror which is a wonderful library for building rich text editors.</p>

<p>And on <a href="https://twitter.com/pretzelhds/status/1236402270373318667">March 7, 2020</a> I finally got fed up enough to start
writing code. (Although there were <a href="https://twitter.com/pretzelhds/status/1226198239289978880">earlier</a> <a href="https://twitter.com/pretzelhds/status/1235257841088368640">indicators</a>
of what was about to happen).</p>

<h2>Development</h2>

<p><a href="https://notebag.app">Notebag</a> is an Electron-based app since I am primarily a web developer and native code seems scary. I would have loved
to give Swift a try, but in the end stuck to what I know best. For the frontend I landed on Vue since that is what Tiptap
uses and I don't really hold much of a strong opinion in the great war of the frontend frameworks.</p>

<p>One great thing that allowed development to move quickly is Prosemirrors concept of input rules. These are essentially
little functions that take a regex you want to search for and then spit out appropriate HTML for the thing you just entered.
This is what powers all of the Markdown formatting as well as a few custom functionalities such as the nestable categories
and Zettelkasten-type links.</p>

<p>For most of March I was still working on this very sporadically as I was pre-occupied with contracting work for most of my
week. Sadly due to the world going a bit haywire I ended up having not so much contract work anymore by April at which point
I started focusing on the development in earnest.</p>

<p>Within a week or so I had implemented most of the barebones features of a note taking app (funnily enough, switching between
notes was one of the last ones. Oops!) And I sent out my first beta builds. At this point the app <a href="https://twitter.com/pretzelhds/status/1246074902941057024">looked a bit like a boring Apple Notes clone.</a></p>

<p>This is what I sent out to a few trusted beta testers</p>

<h2>Differentiating the app</h2>

<p>This was also the time at which I started drilling down on what I consider the unique selling points of the app.
At first there is the omnibar or "Go To Anything". This is essentially the same thing as in any modern IDE or code editor.</p>

<p><x-image
    src="https://pbs.twimg.com/media/EVROgAKWkAAIvT8?format=jpg&name=large"
    alt="Omnibar"
/></p>

<p>You have a full fuzzy search for your notes. And this is also where the keyboard focus really took overhand. I spent a
good chunk of the next two weeks assigning <code>tabindex</code>es to every important element, writing countless <code>:hover, :active, :focus</code>
styles and adding numerous keyboard bindings that you can use to get around the app.</p>

<p>There was lots of talking with beta testers and refining and implementing and polishing. You never expect how much there
is to do until you start writing it down and ending up with a todo list in the mid double digits.</p>

<h2>How do I even marketing</h2>

<p>Once the app reached a reasonably stable level of maturity, I had to get around to the point I am the worst at. The one
I had pushed away again and again for as long as I could: I had to build <a href="https://notebag.app">a landing page</a> to market 
this thing. Oh dear</p>

<p>The struggles started with naming. In total I went through 27 names before the right one appeared. Some of them were:
Keynote, Typemark, Markflow, Swiftnote, Feathermark, Keynib, and Crosskey. The final name wasn't even my idea. The credit
for it goes to <a href="https://twitter.com/h0h0h0">Shaun Farrugia</a>. But I liked it and it stuck.</p>

<p>Next came logo design. I've always had a certain love for RPGs and I liked the way the old timey bags full of gold looked.
It's not a perfect fit for the app at hand, but the design came together quickly and I was quite pleased.</p>

<p><img src="https://notebag.app/assets/logo.png" style="width: 300px; display: block; margin: 4rem auto"></p>

<p>And finally the landing page. I grabbed myself a bit of Tailwind and Jekyll and got to work. For what its worth I still 
have no idea how marketing experts do it, but I discussed the copy with people, I polished the experience of reading it 
for the first time, I made sure it looks decent on all the devices I have around and then it got pushed to live.</p>

<p>Since going live it managed to convince three people to get the app, so I guess I'm not doing too bad!</p>

<h2>Launching</h2>

<p>And here we stand, shortly before the big launch. This is really the first time I have built a proper product of my own
and I am excited and also terrified of where the journey goes next. Here's hoping that the name "Notebag" can eventually
stand proudly among the big stars of note taking.</p>

<p>Look for the launch on ProductHunt on Monday, May 4! And in the meantime, feel free to check out Notebag at <a href="https://notebag.app">notebag.app!</a></p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Sat, 02 May 2020 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Managing WordPress from your terminal]]></title>
        <link>https://pretzelhands.com/posts/managing-wordpress-from-your-terminal</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/managing-wordpress-from-your-terminal</guid>
        <description><![CDATA[A quick tour of WP CLI to make WordPress management fun!]]></description>
        <content:encoded><![CDATA[<p>Hi, I'm Richard. I'm addicted to WordPress.</p>

<p>...</p>

<p>Oh, this isn't the support group? Fine. But nevertheless, let me tell you about a humble command-line tool called <code>wp</code> (or WP CLI). It is by far my favorite way to manage WordPress installations and saves me from having to click through the Dashboard approximately 95% of the time.</p>

<h3>Installation</h3>

<p>Getting <code>wp</code> installed isn't much of a hassle. If you already have <a href="https://getcomposer.org">Composer</a> installed it's a breeze. You need only require the package and you're done.</p>

<p>I usually have <code>wp</code> installed globally because I manage a lot of WordPress sites:</p>

<pre><code class="language-bash">composer global require wp-cli/wp-cli
</code></pre>

<p>But of course you can also just require <code>wp</code> for a project locally:</p>

<pre><code class="language-bash">composer require wp-cli/wp-cli --dev
</code></pre>

<p>If you don't have Composer installed, there's also some <a href="https://make.wordpress.org/cli/handbook/installing/">alternative installation methods.</a> Feel free to pick the one that best suits your workflow.</p>

<p>✨ Congratulations! Your life just became a whole lot more efficient!</p>

<h3>Installing Wordpress from the command-line</h3>

<p>Now to see just what you can do with <code>wp</code>, let's install WordPress and configure it. All done from the cozy comfort of your shell. The very first thing we need to do is to download the latest version of WordPress.</p>

<pre><code class="language-bash">$ wp core download

Downloading WordPress x.y.z (en_US)...
md5 hash verified: 83bec78836aabac08f769d50f1bffe5d
Success: WordPress downloaded.
</code></pre>

<p>That wasn't hard at all, was it? Now let's setup our <code>wp-config.php</code> with all the necessary information. For this you can run the following command</p>

<pre><code class="language-bash">$ wp config create --dbname=wpdemo --dbuser=root --dbpass=root

Success: Generated 'wp-config.php' file.
</code></pre>

<p>Of course you should replace the above database information with your own. The next step is to actually run the WordPress installer. That looks a bit like this</p>

<pre><code class="language-bash">$ wp core install \
          --url="https://wpdemo.test" \
          --title="WordPress Demo Site" \
          --admin_user="pretzelhands" \
          --admin_password="awesome-p4ssw0rd" \
          --admin_email="hello@pretzelhands.com"

Success: WordPress installed successfully.
</code></pre>

<p>And you're done. You can now visit the URL of your WordPress site and take a look at your wonderful new installation!</p>

<p>That's a lot of parameters to remember. But never fear, <code>wp</code> is here! Until you remember all these parameters you can also run <code>wp config create --prompt</code> and <code>wp core install --prompt</code>. These commands will interactively ask you for all required parameters. Nifty!</p>

<p>Now let's talk about some other awesome features you might want to have.</p>

<h3>Installing plugins</h3>

<p>Check! Grab the slug of a plugin and you can run the following command:</p>

<pre><code class="language-bash">$ wp plugin install advanced-custom-fields

Installing Advanced Custom Fields (x.y.z)
Downloading installation package from https://downloads.wordpress.org/plugin/advanced-custom-fields.x.y.z.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.
Success: Installed 1 of 1 plugins.

$ wp plugin activate advanced-custom-fields
</code></pre>

<p>You can also pass in a <code>--activate</code> flag to activate the plugin immediately. This looks like</p>

<pre><code class="language-bash">$ wp plugin install advanced-custom-fields --activate
</code></pre>

<p>If you don't know the slug of a plugin you can find it by going to the <a href="https://wordpress.org/plugins/">plugin repository</a>,
searching for your plugin and then checking what it says after the last slash.</p>

<p>For example the URL for the Advanced Custom Fields plugin looks like this: <code>https://wordpress.org/plugins/advanced-custom-fields</code>
If you check everything after the last slash you can see the slug is <code>advanced-custom-fields</code>.
This is what you pass to <code>wp</code>. It may take some time to remember what slug each plugin has, but you'll remember your favorites soon enough!</p>

<h3>Installing languages</h3>

<p>I hail from a German-speaking country. If I hand over a WordPress site with an English dashboard
to a local client, they'll probably look at me weird. Thankfully, installing languages with <code>wp</code> is a breeze.</p>

<pre><code class="language-bash">$ wp core language install de_DE --activate

Downloading translation from https://downloads.wordpress.org/translation/core/x.y.z/de_DE.zip...
Unpacking the update...
Installing the latest version...
Translation updated successfully.
Success: Language installed.
Success: Language activated.
</code></pre>

<p>And you're done! Go grab some tea or coffee and enjoy your success. The <code>--activate</code> flag
here works the same way it does with plugins.</p>

<h3>Installing a theme</h3>

<p>By now I'm sure you've figured out the pattern. But here's how you install a theme
and activate it</p>

<pre><code class="language-bash">$ wp theme install storefront --activate

Installing Storefront (x.y.z)
Downloading installation package from https://downloads.wordpress.org/theme/storefront.x.y.z.zip...
Unpacking the package...
Installing the theme...
Theme installed successfully.
Activating 'storefront'...
Success: Switched to 'Storefront' theme.
Success: Installed 1 of 1 themes.
</code></pre>

<p>And now you have Storefront for WooCommerce installed and running. Yes, that's
really all there is to it.</p>

<h3>Closing notes and further reading</h3>

<p>The three examples given above showed you the most common use cases for <code>wp</code>, but
you can manage pretty much anything with it. Here's a short excerpt of what you can
do without ever touching the WordPress dashboard</p>

<ul>
<li>Manage users</li>
<li>Create, update, delete your posts</li>
<li>Create, update, delete and moderate comments</li>
<li>Do a search-replace in your database</li>
<li>Export and import WordPress data</li>
<li>Manage multi-site networks</li>
<li>Scaffold code for custom post type, taxonomies, child themes, etc</li>
<li>Run a custom PHP shell for your WordPress install</li>
<li>Run a local development server for your Wordpress install</li>
<li>... (The list <strong>does</strong> go on for a while longer)</li>
</ul>

<p>And if you install WooCommerce for example you get another set of commands
related just to managing your online store.</p>

<p>So if you're working with WordPress, especially if you're working with more than
2-3 sites I heartily recommend you go out, install <code>wp</code> and get more free time out
of your day.</p>

<p>If you want to read more, you can <a href="https://make.wordpress.org/cli/handbook/">check out the documentation</a>,
it is an excellent reference. Otherwise, a lot can be found out by running <code>wp help</code> and browsing the
man page.</p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Tue, 29 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Live streaming as productivity multiplier]]></title>
        <link>https://pretzelhands.com/posts/streaming-as-productivity-multiplier</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/streaming-as-productivity-multiplier</guid>
        <description><![CDATA[A short tale about how live streaming helps me get more done in less time]]></description>
        <content:encoded><![CDATA[<p>There's probably a few of you who know some form or another of this meme</p>

<p><x-image
    src="streaming-01.png"
    alt="20 minutes of coding, 5 hours of coffee breaks - Perfectly balanced"
/></p>

<p>Sometimes this is very much me when it comes to side projects. Don't get me wrong, I love building side projects and really want to get as many things out there as possible. But sometimes the rabbit holes of the internet can be crazy deep and what starts out as a quick check in the documentation can end up in hours of reading about some esoteric programming language you'll never make use of.</p>

<p>I admit that I might have a bit of a procrastination problem. It happens to the best of us.</p>

<p>But there's a nice way to fight it and share with other people at the same time: Live streaming while you code. This has been a thing for a while now, but especially since <a href="https://shipstreams.com">shipstreams.com</a> by fellow Austrian programmer <a href="https://twitter.com/arminulrich">Armin Ulrich</a> showed up, there's been a bit of a boom around it. And I joined in back when it first started.</p>

<p>Turns out streaming is a bit like pair programming on demand but with more of a social factor. Having someone watch you and help you when you're stuck is like a super power. At the same time you get to talk about what you're doing and interact with people who are actually interested in what you're doing feels both incredibly humbling and motivating.</p>

<p>Generally there's only like 4-5 people at any one time watching my stream, but knowing that they are spending their free time to see me code makes me a lot more focused and prevents me from accidentally wandering off on tangents of non-productive behavior. In the end it's a bit more exhausting than coding by yourself, but it also feels like I get at least twice as much done in the same time.</p>

<p>Also whenever you find an error you will be doubly invested to fix it and to keep things moving along. Yes, your code might not always be super pretty while you're live, but at least you get stuff done! And sometimes that's the important thing. You can fix code that has been written, but you can't do anything with code that does not exist.</p>

<p>So if you ever find yourself in a creative rut, give streaming a shot! Set up an account on Twitch and Shipstreams, <a href="https://shipstreams.com/blog/recommended-twitch-streaming-setup-guide">learn how to set up your PC/laptop</a> for streaming and off you go! It doesn't have to be perfect, it doesn't have to be ultra professional. People will be happy enough to watch you either way. It's great fun and maybe you can learn a thing or two.</p>

<p><strong>And for a shameless plug: You can <a href="http://pretzelhands.live">follow me on Twitch</a> to see when I go live. I'd love to chat with all of you!</strong></p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Sun, 20 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Breaking the rules to keep programming fun and experimental]]></title>
        <link>https://pretzelhands.com/posts/breaking-the-rules</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/breaking-the-rules</guid>
        <description><![CDATA[A dive into how I sometimes deliberately ignore coding standards just to enjoy myself.]]></description>
        <content:encoded><![CDATA[<p>You probably have processes at work. Style guides for writing your code. Unit tests and integrations tests verifying each piece of functionality. Best practices established in the project over a commit history in the thousands. The sacred rules established in your company and by the programming community at large. And following those rules is important for a product used by potentially thousands or millions of users.</p>

<p>But sometimes, in your personal projects, you should try throwing the rules out of the window. Just chuck them out and play around! One such example is the code <a href="https://pretzelhands.com">behind my blog</a>. It's written in PHP without a framework of any kind. It's a loose collection of loose PHP files with a bunch of rewrite rules holding it together. It's basically like a non-polished version of Jekyll.</p>

<p>Here's what the folder structure looks like using <code>tree</code></p>

<pre><code class="language-xml">blog
├── bootstrap.php
├── composer.json
├── composer.lock
├── lib
│   └── Post.php
├── ps
│   ├── 2019-01-02-coloring-terminal-text.md
│   ├── 2019-01-03-non-obvious-behaviors.md
│   ├── 2019-01-04-command-line-flags.md
│   ├── 2019-01-05-bringing-your-entire-infrastructure-down.md
│   ├── 2019-01-07-jinx.md
│   ├── 2019-01-09-how-i-got-into-freelancing.md
│   ├── 2019-01-12-mentoring-junior-team-members.md
│   ├── 2019-01-14-building-responsive-email-with-mjml.md
│   └── 2019-01-15-breaking-the-rules.md
├── public
│   ├── assets/ (images etc.)
│   ├── blog.php
│   └── index.php
├── vendor/ (dependencies)
└── views
    ├── base.mustache
    ├── blog.mustache
    ├── listing.mustache
    └── partials
        ├── about-me.mustache
        ├── description.mustache
        ├── header.mustache
        ├── newsletter-form.mustache
        └── title.mustache
</code></pre>

<p>The <code>public</code> folder is what gets exposed. The <code>ps</code> folder contains all of my posts written in Markdown with frontmatter. The <code>lib</code> folder holds the class responsible for loading posts.</p>

<p>Internally a URL like <code>https://pretzelhands.com/posts/jinx</code> gets rewritten to <code>https://pretzelhands.com/blog .php?slug=jinx</code>. I then search the files to match the correct one based on the slug. I parse the post date from the title. All of that happens in a Post class. Before that the code was directly in blog.php along with some nasty mixed up HTML.</p>

<p>Here's what that looked like</p>

<p><x-image
    src="rules-01.png"
    alt="The first version of the post-fetching code"
/></p>

<p>Is it ugly code? Unbelievably so. Was it fun to write? You bet! The entire blogging system was running in an hour or two on a Sunday before going back to work. I didn't want it to be pretty, I just wanted to get it out there. And whenever some code becomes unbearable to look at some selective refactoring happens.</p>

<p>And I love hacking on this thing. I bolt on some code, upload it to my server and call it a day. There's some globals running wild, there's code duplication, there's all sorts of stuff I would get yelled at for during a code review. But no one will ever see the code for my little blog, so I'm totally okay with doing this. It's my own messy little playground. I keep adding to it and see what sticks.</p>

<p>So if you're ever working on a small project of your own that isn't intended to be The Next Big Thing™, why don't you just go all out and write code as if you're throwing color at the wall? It's incredibly therapeutic, I tell you!</p>

<p>Here's an example from a project didn't see the light of day</p>

<pre><code class="language-php">require_once('../../server/bootstrap.php');

use Respect\Validation\Validator;
use Carbon\Carbon;

$database = getDatabaseConnection();
$emailValidator = Validator::email();

if (!isset($request-&gt;email) || !$emailValidator-&gt;validate($request-&gt;email)) {
    sendResponse([
        'error' =&gt; true,
        'errors' =&gt; [
            'Please provide a valid email for logging in.'
        ],
        'errorFields' =&gt; [
            'email'
        ]
    ], 400);
}
</code></pre>

<p>There's no standardization to any of this. The <code>sendResponse</code> function was a little wrapper that just sent a few headers, serialized the array into JSON and echoed it all. <code>$request</code> is just the <code>STDIN</code> PHP provides wrapped in <code>json_decode</code> It's beautiful in its ugliness.</p>

<h2>Disclaimer</h2>

<p>This does not mean I think you should write production code as if you're some abstract artist and ignore all rules and conventions at work. Those conventions usually have good reasons behind them. But I feel like if you're programming for your own enjoyment it helps to be more creative and experimental. To get rid of rigid structures. Who knows, maybe you find something you really like and can refine and improve.</p>

<p>So don't go around and tell people that Richard gave you the permission to write spaghetti code. 😁</p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Tue, 15 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Building responsive email with MJML]]></title>
        <link>https://pretzelhands.com/posts/building-responsive-email-with-mjml</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/building-responsive-email-with-mjml</guid>
        <description><![CDATA[A short introduction to the state of HTML email and how to build one easily]]></description>
        <content:encoded><![CDATA[<p>So I assume that if you're reading this article, you belong to one of three categories of people: The type which has never built HTML email but wants to get into it, the type which has built HTML email before and wants to find a better way to do it and then the type who just reads everything I write for some reason.</p>

<p>If you count yourself among those that have never built an HTML email before, <a href="https://www.emailonacid.com/blog/article/email-development/email-development-best-practices-2/">count your blessings</a>. The amount of work that has to go into these magical creations can be absolutely mind-numbing. HTML email is like <a href="https://www.campaignmonitor.com/dev-resources/guides/coding-html-emails/">the Wild West of web technologies</a>.</p>

<p>I wish I was kidding about this, I really do. Building HTML email was one of my responsibilities at my first job before <a href="https://pretzelhands.com/posts/how-i-got-into-freelancing">I got into freelancing</a> and I feel like in those three years I aged by at least a decade. The amount of tiny, obscure things you have to remember is incredible. An affront to everyone even. We're talking inline styles, table layouts, transparency hacks, and so and so forth. And funnily enough Google, the company responsible for one of the most advanced browers, is one of the worst offenders.</p>

<p>No really, <a href="https://www.campaignmonitor.com/css/">here's a list of CSS support in email</a>. Be sure you sit well before reading it. It's a hoot and a half.</p>

<p>Of course for a while now there's been solutions like Foundation for Emails, emailframe.work and many others. But they're either terrible to set up or still require you to remember a good few pitfalls in HTML email. But a while ago, thanks to <a href="https://twitter.com/pugson">@pugson</a> I learned about one framework that stood above all others.</p>

<p><a href="https://mjml.io">It's called MJML and it rocks my socks.</a></p>

<p>It came a bit too late for my mainstay in email marketing, but I still use it for transactional emails. And this is your whirlwind guide to building HTML with it! So let's get started.</p>

<h2>Installing and running MJML</h2>

<p>Installing MJML is quite simple if you have <code>npm</code> or <code>yarn</code> installed. You need only run the following command and you're good to go.</p>

<pre><code class="language-bash">npm install -g mjml
</code></pre>

<p>If you're not part of the web crowd and don't have MJML or if you just want to try it out and save some time, you can also use <a href="https://mjml.io/try-it-live">their online playground</a>. I admit that I mostly use that one, simply because it's super convenient to see your email right next to your code.</p>

<p>To learn how to use the command-line tool in detail, be sure to check out <a href="https://mjml.io/documentation/#command-line-interface">the excellent documentation.</a></p>

<p>But as quick summary here are the commands you will most likely want to use</p>

<pre><code class="language-bash"># Compile the MJML file and save it to an HTML file
mjml email.mjml -o email.html

# Watch an MJML file for changes and recompile automatically
mjml --watch email.mjml -o email.html

# Minify the compiled HTML to save space
mjml --config.minify email.mjml -o email.html
</code></pre>

<h2>Building your first email</h2>

<p>As an example email to build I will pick one made by yours truly. It's a magic link email I'm using for <a href="https://howmuchdoesthismeetingco.st?ref=blog">a meeting tracker app</a> I'm currently building.</p>

<p><x-image
    src="mjml-01.png"
    alt="Magic link email layout"
/></p>

<p>It's not particularly complex, so I think it's a perfect fit for a first go at MJML. First, let's take a look at the entire template as it stands and then let's take it apart bit by bit.</p>

<pre><code class="language-xml">&lt;mjml&gt;
    &lt;mj-head&gt;
        &lt;mj-font name="Montserrat" href="https://fonts.googleapis.com/css?family=Montserrat:400,700" /&gt;
        &lt;mj-preview&gt;Click the button inside to log into your account&lt;/mj-preview&gt;

        &lt;mj-attributes&gt;
          &lt;mj-text font-size="16px" line-height="24px" padding="0px 48px 16px" /&gt;
          &lt;mj-button font-weight="bold" font-size="16px" background-color="#8e2de2" /&gt;
          &lt;mj-divider border-color="#8e2de2" /&gt;
          &lt;mj-all font-family="Montserrat, Helvetica, Arial, sans-serif" /&gt;
        &lt;/mj-attributes&gt;

        &lt;mj-style inline="inline"&gt;
          .link {
            color: #8e2de2;
            text-decoration: none;
          }
        &lt;/mj-style&gt;
    &lt;/mj-head&gt;
    &lt;mj-body&gt;
        &lt;mj-section padding-bottom="0"&gt;
            &lt;mj-column&gt;
                &lt;mj-image
                        padding-top="20px"
                        padding-bottom="20px"
                        width="200"
                        src="https://howmuchdoesthismeetingco.st/assets/logo-hmdtmc.png"
                /&gt;

                &lt;mj-divider /&gt;

                &lt;mj-text align="center"&gt;
                    &lt;h1&gt;Your magic link&lt;/h1&gt;
                &lt;/mj-text&gt;

                &lt;mj-text&gt;
                    Somebody requested a log in from your account. If that was you click the button
                    below to sign into your account.
                &lt;/mj-text&gt;

                &lt;mj-button href="https://howmuchdoesthismeetingco.st/token/abcdefg123456789"&gt;
                    Log in now
                &lt;/mj-button&gt;


                &lt;mj-text&gt;
                    &lt;br /&gt;
                    Have a wonderful day,&lt;br /&gt;
                    &lt;strong&gt;Richard from HMDTMC&lt;/strong&gt;
                &lt;/mj-text&gt;

                &lt;mj-divider /&gt;

                &lt;mj-text
                        padding="8px 48px"
                        font-size="11px"
                        line-height="16px"
                        color="#999999"
                &gt;
                    If the button up doesn't work, you can use this link:
                    &lt;a
                            href="https://howmuchdoesthismeetingco.st/token/abcdefg123456789"
                            class="link"
                    &gt;
                        https://howmuchdoesthismeetingco.st/token/abcdefg123456789
                    &lt;/a&gt;
                    &lt;br/&gt;
                    Your login link expires after 1 hour. You can request another one at any time.
                &lt;/mj-text&gt;
            &lt;/mj-column&gt;
        &lt;/mj-section&gt;
    &lt;/mj-body&gt;
&lt;/mjml&gt;
</code></pre>

<p>Okay, that was a lot of code and if you're not running yet, I'm proud of you. The most complex part is probably right at the beginning in <code>mj-head</code>, but let's start from the very beginning: the root element.</p>

<h3>The document structure</h3>

<p>MJML is not unlike HTML or XML. It is after all a markup language that tries to spit out HTML. As such, each MJML document begins with a simple root tag:</p>

<pre><code class="language-xml">&lt;mjml&gt;&lt;/mjml&gt;
</code></pre>

<p>Inside this root element you have the same sections you're used to from a regular HTML document. One <code>head</code> and one <code>body</code>. They're just prefixed with <code>mj</code>. So the standard structure looks like so:</p>

<pre><code class="language-xml">&lt;mjml&gt;
    &lt;mj-head&gt;&lt;/mj-head&gt;
    &lt;mj-body&gt;&lt;/mj-body&gt;
&lt;/mjml&gt;
</code></pre>

<p>The <code>mj-head</code> is a pretty exciting place where you define all of your styling and meta data! It's probably the most fun part of writing an MJML-based email.</p>

<h3>The document head</h3>

<p>Now let's look at all the weird stuff in the <code>mj-head</code> and demystify it a bit.</p>

<pre><code class="language-xml">&lt;mj-head&gt;
    &lt;mj-font name="Montserrat" href="https://fonts.googleapis.com/css?family=Montserrat:400,700" /&gt;
    &lt;mj-preview&gt;Click the button inside to log into your account&lt;/mj-preview&gt;

    &lt;mj-attributes&gt;
      &lt;mj-text font-size="16px" line-height="24px" padding="0px 48px 16px" /&gt;
      &lt;mj-button font-weight="bold" font-size="16px" background-color="#8e2de2" /&gt;
      &lt;mj-divider border-color="#8e2de2" /&gt;
      &lt;mj-all font-family="Montserrat, Helvetica, Arial, sans-serif" /&gt;
    &lt;/mj-attributes&gt;

    &lt;mj-style inline="inline"&gt;
      .link {
        color: #8e2de2;
        text-decoration: none
      }
    &lt;/mj-style&gt;
&lt;/mj-head&gt;
</code></pre>

<p>As the very first child of our document head we have a <code>mj-font</code> tag. It takes in a <code>name</code> and an <code>href</code>. If you look at where the <code>href</code> links to, you'll quickly figure out that all this does is load in a font hosted on Google Fonts. Of course, given the state of affairs, not all email clients support this. But some do and they get rewarded with prettier typography.</p>

<pre><code class="language-xml">&lt;mj-font name="Montserrat" href="https://fonts.googleapis.com/css?family=Montserrat:400,700" /&gt;
</code></pre>

<p>After this we find an <code>mj-preview</code> tag that contains some text. This is an invisible preview text that gets displayed in the inbox of some email clients right below the subject of the email. To give you some context, I'm talking about the light-gray text depicted here (in Apple Mail)</p>

<p><x-image
    src="mjml-02.png"
    alt="An example of preview text"
/></p>

<p>You can use this to tease your subscribers about the content of your newsletter or give a summary of the contents. It's a bit of an art to find text that is just long enough to fill out the entire area, but not too long to be cut off by the email client.</p>

<pre><code class="language-xml">&lt;mj-preview&gt;Click the button inside to log into your account&lt;/mj-preview&gt;
</code></pre>

<p>Next up is a special one. <code>mj-attributes</code> allows you to set default styling for all MJML tags that you're using. You can of course override these at any time. These just exist so you don't have to repeat yourself over and over again.</p>

<p>Each child of the <code>mj-attributes</code> tag is another MJML tag such as <code>mjml-text</code> or <code>mjml-button</code>. Each of the CSS properties you want to use is defined as an attribute. This may be unusual at first, but you'll get used to it very quickly. There's also a special <code>mj-all</code> tag, which can be used to apply a style to all elements. (A bit like the CSS <code>*</code> selector)</p>

<p>Also note how I define some fallback fonts for the <code>mj-all</code> tag. This is so that email clients with bad CSS support still have at least a somewhat bearable font to look at.</p>

<pre><code class="language-xml">&lt;mj-attributes&gt;
    &lt;mj-all font-family="Montserrat, Helvetica, Arial, sans-serif" /&gt;
    &lt;mj-text 
        font-size="16px"
        line-height="24px"
        padding="0px 48px 16px" 
    /&gt;
&lt;/mj-attributes&gt;
</code></pre>

<p>And we wrap up our head component with an <code>mj-style</code> tag. You can use these to write regular CSS and media queries. One special thing to note here is that I added the <code>inline="inline"</code> attribute to my style tag. This makes MJML search for all elements with a class of <code>link</code> and adds a style attribute to it.</p>

<pre><code class="language-html">&lt;!-- So this element --&gt;
&lt;a href="#" class="link"&gt;My awesome link!&lt;/a&gt;

&lt;!-- Turns into this --&gt;
&lt;a href="#" class="link" style="color: #8e2de2; text-decoration: none;"&gt;My awesome link!&lt;/a&gt;
</code></pre>

<p>The reason why you would want this is once again rooted in the lack of CSS support in email clients. Many only support only basic selectors like <code>a</code> or <code>h1</code>, while Gmail just takes your <code>style</code> tag and <strong>throws it out entirely.</strong> Yup. That's actually a real thing that happens. Thanks Google.</p>

<p><x-newsletter /></p>

<h3>The document body</h3>

<p>This is where the meat of your email lives. MJML provides many custom tags to make building email as painless as can be. You can find a list of all tags available <a href="https://mjml.io/documentation/#standard-body-components">in the documentation.</a></p>

<p>I will highlight the ones I used below, starting with <code>mj-section</code>.</p>

<p><code>mj-section</code> is meant to be used as a row in your layout. Imagine it a bit like Bootstraps or Foundations grid row elements. It offers some vertical spacing by default, but you can of course remove it. If you don't need any special thing like that you can of course also just use a <code>div</code> or <code>table</code> (and yes, I'm serious about the <code>table</code>).</p>

<p><code>mj-column</code> allows you to define one or more columns in your layout. This way you can easily build a grid layout in your email. To have multiple columns next to each other put them into an <code>mj-section</code> and you're good to go.</p>

<p>An example of a 2x2 grid could look something like this</p>

<pre><code class="language-xml">&lt;mj-body&gt;
    &lt;mj-section&gt;
        &lt;mj-column&gt;Row 1, Column 1&lt;/mj-column&gt;
        &lt;mj-column&gt;Row 1, Column 2&lt;/mj-column&gt;
    &lt;/mj-section&gt;

    &lt;mj-section&gt;
        &lt;mj-column&gt;Row 2, Column 1&lt;/mj-column&gt;
        &lt;mj-column&gt;Row 2, Column 2&lt;/mj-column&gt;
    &lt;/mj-section&gt;
&lt;/mj-body&gt;
</code></pre>

<p><code>mj-image</code> is an image. You probably knew that. By default MJML adds some styling to it to make it stand out a bit more, but once again, you can easily change that.</p>

<p><code>mj-text</code> is a generic text container. You can put any <code>p</code>, <code>h1</code> or whatever else in there and make it look pretty. If it's just a random blob of text you can also simply put that text in directly as a child.</p>

<p><code>mj-button</code> is essentially a link that's styled like a button. Pass in <code>href</code> as an attribute and you've got a great call-to-action in less than 30 seconds. If that's not speedy email development I don't know what is.</p>

<p><code>mj-divider</code> is a bit like an <code>hr</code>, except it's a lot easier to style. In my email template I used it to separate header and footer from the main content.</p>

<h3>Styling tags</h3>

<p>For each of the tags I named above, you can easily pass in attributes to give them a one-off style that you didn't add in your <code>mj-attributes</code>. For example I used this tag to style my footer text</p>

<pre><code class="language-xml">&lt;mj-text
    padding="8px 48px"
    font-size="11px"
    line-height="16px"
    color="#999999"
&gt;
    Footer text
&lt;/mj-text&gt;
</code></pre>

<p>If you end up using a set of styles more than once, but it is still not the default style you want, you can also define an <code>mj-class</code> that allows you to pass styles around like Oprah passes around cars.</p>

<p>You need only define the following in your <code>mj-attributes</code></p>

<pre><code class="language-xml">&lt;mj-attributes&gt;
    &lt;mj-class name="padded" padding="20px" /&gt;
&lt;/mj-attributes&gt;
</code></pre>

<p>Give you class a name and some CSS attributes you want it to use and off you go. <strong>Note however that this only works for MJML tags</strong>. For regular HTML tags you should make use of <code>mj-style</code>.</p>

<h2>Summary</h2>

<p>MJML is a great tool that helps you build responsive HTML email much faster than you would otherwise be able to. The predefined components help you get the most annoying parts out of the way so you can focus on the content and layout, instead of fiddling around with CSS hacks.</p>

<p>Of course, no tool is perfect and neither is MJML. So in the end, always be sure to test your email templates with a tool such as <a href="https://litmus.com/">Litmus</a> or <a href="https://www.emailonacid.com/">EmailOnAcid</a>. Just to be sure ;-)</p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Mon, 14 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[My approach to mentoring junior team members]]></title>
        <link>https://pretzelhands.com/posts/mentoring-junior-team-members</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/mentoring-junior-team-members</guid>
        <description><![CDATA[Thoughts and musings on mentoring others and always trying to do my best]]></description>
        <content:encoded><![CDATA[<p>This post is probably a bit of a weird one. I generally don't talk about my soft skills as I hold the opinion that I am an awkward nerd who sometimes can't even keep English and German apart in his head and thus makes hilariously bad code switching a reality.</p>

<p>On the other hand, I've recently found myself doing a lot of mentoring during my hours at <code>$BIGCO</code> that I mentioned 
in <a href="/posts/how-i-got-into-freelancing">my previous post about freelancing</a> and the people I've been speaking with seemed to be into it.</p>

<p>Things I write here may be complete bollocks, but hey, read the first paragraph again and you'll understand why. I guess this is partly just for me to think things through.</p>

<h2>Be patient with everyone</h2>

<p>I don't know if you've ever noticed this with other members in your team, but many people seem oddly annoyed when approached with questions by someone. It may be because they dislike being interrupted by others during work, it may be because they don't like being bothered with someone else's problems. But it's always a bit sad to see when someone has a question and the person they ask rolls their eyes or sighs as they get up from their table.</p>

<p>Hence I always, always, <strong>always</strong> try to be patient. I've had many people come to me for a question that took me all of a minute to show them the answer to. They got unstuck and could happily continue their work and I didn't lose any critical amount of productivity in mine. If a question in your team pops up a lot, write it down on an FAQ page.</p>

<p>And if you ever find yourself losing your train of thought, <a href="https://swizec.com/blog/write-down-everything">go and read this post by Swizec.</a> No one will hold it against you if you take a few seconds to write down stuff.</p>

<h2>Give them tools to solve the problem</h2>

<p>One thing that can be nice to do is to not point somebody directly to a solution unless it's critical to your deadline. But if you have leeway in your project try and show them how they can find a solution themselves. They don't know how to get started with checking where things go wrong in their program? Teach them how to use breakpoints to step through their program! They can't figure out why their <code>flexbox</code> is behaving weirdly? Show them a site like <a href="https://flexbox.help">flexbox.help</a> let them describe their goal to you and let them click through it while explaining it to you.</p>

<p>If they really ran into something bigger they'll come back and ask for help again, but if they can figure it out themselves that feels like a huge win that they had independently of you. It gives a sense of confidence and independence.</p>

<p>So show them how to get there, but don't just point at the screen and tell them, they should have, obviously used <code>justify-content: space-between</code> instead of <code>justify-content: space-around</code>. Let them have their own wins.</p>

<h2>Pair programming can help a ton</h2>

<p>This is of course dependent of the type of person you're dealing with, but sometimes  of people feel a lot more confident when pair programming. At times it already helps if you're the duck for their rubber duck debugging. While they speak out loud and explain the code to you, often times things will click in their head.</p>

<p>So if they're open to it and you have a few minutes, sit with them. Let them explain their code, walk through it with them. Sometimes the thought of someone having your back is all that is needed.</p>

<h2>Listen to the individual</h2>

<p>Of course, all of this varies from person to person. Junior devs that joined your team a week ago will most likely need more hand-holding than one who joined two months ago. Some people don't like somebody looking over their shoulder while they code, and that's fine too. For what it's worth I'll fumble through a <code>docker run</code> command if you just stand their and watch me.</p>

<p>Each person is different and each one requires a different approach to mentoring. But these are some things I found useful in helping others learn. Maybe you can find some use for yourself.</p>

<p>And above all, when a question seems trivial to you but insurmountable to your junior always remember: Once upon a time you were new to programming as well. ;)</p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Sat, 12 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[How I got into freelancing]]></title>
        <link>https://pretzelhands.com/posts/how-i-got-into-freelancing</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/how-i-got-into-freelancing</guid>
        <description><![CDATA[The story how I got into freelancing and managed to get to the point I'm at today.]]></description>
        <content:encoded><![CDATA[<p><strong>TL;DR: I jumped into freelancing with blind optimism, almost went broke for it and had just the right amount of luck
at just the right time to turn it all around.</strong></p>

<p>Today's blog entry was <a href="https://twitter.com/_pretzelhands/status/1082762244893691906">decided on by Twitter</a>, so for the
people who are interested, here's the story of how I got into freelancing from the beginning all the way up to where we
are now.</p>

<p>Since there may be a few people reading who are coming here for the first time, I'll give you some basic introduction of
who I am, what my experience level is etc. Here goes nothing.</p>

<p>I'm Richard, currently 21. I started messing with HTML and CSS around October 2011 by sheer accident. Somewhere
along the way I picked up JavaScript and I started messing with PHP in early 2013.</p>

<p>For all intents I have no real educational background. I dropped out of high school in sophomore year (2013) and decided 
to pick up a vocational training as media designer instead. As part of that I visited vocational school which is four
grades consisting of 3x9 and 1x4 weeks of schooling. That's the absolute peak of my education. My highest achievement
along traditional academia is a middle school graduation.</p>

<p>No, I'm not kidding. I've never learned about sine and cosine or anything like that. I know that if I enter a sine function
in my calculator I get an even wave form. No idea what that's even useful for.</p>

<p>As of writing this post I'm entering my third year of business. Last year I finished with approximately $70,000 in gross
revenue. Projections for this year are higher. Now, enough braggy stuff, let's get onto the meat of the story.</p>

<h3>Q4 2016: Finishing my vocational training and moving across the country</h3>

<p>This is where our story starts. In October 2016 I was about two months from the final exam for my vocational training.
By then I had made friends with various colleagues and one in particular. There won't be any names for privacy reasons, 
but she suggested to team up and try going freelance. She would do the marketing and I would do the coding.</p>

<p>So that's what we did. Around the middle of October we both had an appointment at the Chamber of Commerce and set up
our companies along with a joint company named Marivol. The name was based on <code>mariposa</code> for butterfly and <code>volare</code>
for <code>to fly</code>.</p>

<p>At the same time I was about to move cross country from my home in Salzburg to the lovely little province of Burgenland.
The combination of this move, my general being fed up with the company I previously worked at and starting a new company
led me to one decision</p>

<p><strong>"I will quit my job and go freelance full-time"</strong></p>

<p>In hindsight that was probably not the smartest choice but it is what it is. So for the first two months while I was
wrapping up my job situation, my colleague started looking for clients. We put up a website, printed flyers, the whole
nine yards.</p>

<h2>Q1 2017: Big trouble in little Burgenland</h2>

<p>By the time 2017 rolled around we had pulled in a total of four clients. My colleagues idea was to start a bit lower
with pricing due to lack of references. That sounded alright and once everything was settled and the dust cleared we had
our first revenue.</p>

<p><strong>It came out to approximately $1,500. Yup.</strong></p>

<p>Divide that by two and you'll quickly realize that that's not a lot to live off of. I had savings of course and this was
the moment where I was glad I had them. So while my partner continued the search I clenched my teeth and started building.</p>

<p>We finished projects for two of the clients. One client sent us a pre-payment and never replied to a design template we
sent them. One client never managed to send us all the necessary data to set up her web shop. We were truly off to a great
start. Eventually January became February became March. I turned 20 and had made a total of $400 from freelancing, when
a bit of shocking news hit. My colleague couldn't sustain the effort next to her full-time job.</p>

<p>I felt a bit of a bite in my stomach and accepted the fact as it was. The very next day I did the best thing I could think
of: Sign up for UpWork and find me some work. So I set up a profile and got to work.</p>

<p>Turns out that working on Upwork is hard. I mean real hard. I tried to play the numbers game and sent applications for jobs
big and small and I did find some jobs using PHP and JavaScript. I even applied myself and got certified with their talent
program. I added all the things to my profile, I did the tests and scored in the top 10% for as many as I could.</p>

<p>And while it wasn't an overwhelming amount, at least some money started rolling in eventually. By end of March I was up
to approximately 400$/month from freelancing with my first repeat customers. It wasn't huge, but at least it slowed the 
drain on my savings which were growing smaller each month. But the worst was yet to come.</p>

<h2>Q2 2017: Living in the Philippines and going broke</h2>

<p>Eventually April 2017 rolled around and I went ahead with a plan I'd had for a bit more than a year by then. I was going
to visit and live with my girlfriend in the Philippines for three months. This had a few useful benefits:</p>

<ul>
<li>Compared to Austria, staying in Manila is quite cheap</li>
<li>I didn't have to pay household contribution to my parents</li>
<li>I was with my girlfriend (Duh!)</li>
<li>Also the food was good. I love isaw to this day.</li>
</ul>

<p>So on April 15 I flew over and I was supposed to stay until July 15. During that time I kept on searching for scraps and
bits on Upwork and all other sites I could find. I posted on Twitter about much of anything I did, simply as a publicity
tool. But things didn't exactly get much better. I still hovered around 200-400$ of income per month, which was simply not
enough to cover my living costs.</p>

<p>First the evenings out were cut down. Then the weekend trips. Eventually me and my girlfriend shared a single plate of
food at a restaurant in her university to make the money last longer. I was still happy being there, but my time was noticeably
running out.</p>

<p>Eventually me and my girlfriend hopped on a bus and travelled to some places in Northern Luzon, we stayed at the cheapest hotels possible and spent
our evenings eating cheap takeout from 7/11 and watching Gilmore Girls. Two weeks later we returned to Manila when finally
the decision was made that perhaps I should rebook my flight to save whatever I had left and return to home earlier. And so I did.</p>

<p>By this time I had someone reach out to me on Twitter. He asked if I would be willing to help him develop a few things. 
Apparently he had followed me since way back when I still dabbled in game development and had my old job. We were scheduled 
to call for a first time soon after I left the country.</p>

<p>So I ended my little flirt with Digital Nomadism and returned to Austria on July 1. All I had to my name was a lead for
a freelance job and $100. This was the point at which I decided:</p>

<p><strong>"Either this lead works out and keeps me afloat or I'm stopping this."</strong></p>

<h2>Q3-Q4 2017: The Pursuit Of Happiness</h2>

<p>I arrived back at home pretty much hopeless and desperate. I took about a week off to replenish some energy and gather
some confidence and then I went straight back to work. It turned out to be my last week-long vacation for approximately 1.5 years.</p>

<p>And I had a call. And this call went well. It went very well. So I started helping the person who had contacted me with 
developing a shop. I helped with developing a fishing trip portal.</p>

<p>On the side I was doing some work for previous customers. By the end of July 2017 I had somehow managed to scrape together
an income of approximately $1,500. It wasn't a luxurious living, but for the first time in almost a year I had a living.</p>

<p>I paid back my parents for the leeway they gave me and pursued the lead further. It managed to come out to somewhere
around $1,000 to $1,250 per month for this one lead, plus some extra money from other leads. I worked quite a lot during 
that time and developed a pattern of work schedule.</p>

<p>I'd get up around 8am and would work until 1pm for my big lead. I would then take off some time in the afternoon until
maybe 4-5pm and enjoy the sun outside, try and get some of that Vitamin D. I would then return to scheduled calls and
keep on coding until 12am or maybe 1am for other projects. It sounds crazy in hindsight, but damn, that was a happy time
for me. It felt like I finally had achieved something.</p>

<p>And I don't know why, but once I had that first income rolling, other leads started popping up. A former classmate of mine
worked at a local radio station allowing anyone to broadcast their own program. They wanted to relaunch their site from
Typo3 to Wordpress and needed some 800 sites of content migrated. So I took the job. It took me two months next to my
other jobs, but I had tried to high-ball my hourly rate at that point (which was maybe $45/h).</p>

<p><strong>I got almost $5,000 for this project</strong></p>

<p>This was huge to me! Somebody was actually willing to pay me proper money for my time. All said and done, after various
deductions I ended my 2017 just barely below the line at which I have to start paying income tax. For reference, that is
about $12,500 in Austria.</p>

<h2>Q1-Q2 2018: Continued leads and increased income</h2>

<p>So 2018 rolled around. I joined a group I have grown to love and cherish: <a href="https://wip.chat">wip.chat</a> - The group inspired
me and motivated me to do my all each and every day.</p>

<p>By this time thankfully there was just enough word of mouth to keep me going. I worked with my main leads from July 2017
and the local radio station along with some small-time maintenance tasks. All said and done it came out to some $2,000 to
$2,500 per month. I was able to save again. I got to lease myself a new car.</p>

<p>At this point my old colleague from early 2017 also reached out. She had by now switched to working for the parent company
for a few well-known brands and was doing digital marketing. Knowing that I was still around and programming she also generated
a few leads for me that kept contributing to this roll I was own. I developed a little digital marketing asset platform for them.</p>

<p>In the spring my girlfriend came by to visit me in Austria for three months. And we had a good time this time around.
There was no worry of being kicked out of anyone's house or my revenue stream suddenly draining to zero. Life was good.
Also, I got engaged! So that was fun!</p>

<p>And then May 2018 rolled around. A recruiter contacted me on LinkedIn (I know, but hear me out). I responded politely and
said that I would be happy to take on some freelance opportunities even for a longer period of time. Apparently they had
had some trouble finding people to recruit since it was all very short-term.</p>

<p>I ended up driving to Graz and having an interview with the recruiter. I named him my hourly rate that was a good step
above what I charged before (~$70) and he named me the amount of hours that would be approximately required of me each month. 
I agreed and drove home to do some maths. So I punched some numbers into the calculator and had a bit of jaw-dropping moment. 
All the thoughts in my head were:</p>

<p><strong>"Wait a sec, they're not going to pay me $10,000 per month, right? I'm 21. That's ridiculous."</strong></p>

<p>And then I was invited to an interview with that company. And they liked what I had to offer. And they hired me for a
contract between June 2018 and December 2018. Yes. They paid me $10,000 a month.</p>

<h2>Q3-Q4 2018: Holy shit, what is happening</h2>

<p>So I started working at said company for payment integrations and I integrated into their team. I was spending approximately
30h/week in their office and then spent another 10-20h/week doing small maintenance tasks where required. Having a big name
in my portfolio I upped my hourly rate a bit. I added the company to my LinkedIn profile.</p>

<p>The next 6 months passed rather quietly. I drove out to Graz to work and drove back home again. My office times being approximately
8.30am - 3pm followed by some commuting and some work in the evening. My average income varied between $12,000 and $14,000
each month (although those are gross revenue numbers). I just packed everything into a savings account and decided to live
off of $1,500 per month at most.</p>

<p>And each month the money would arrive. And each month I was still shocked. So eventually November came around and the end
of my contract drew near. I had a discussion with my superior at the company and after everything was said and done they
offered to extend my contract for another 8 months, with more hours.</p>

<p>During this time I really didn't have much time nor energy for any side projects, but I did finally take a week of vacation
in November. My first in  a year and a half. And in December we signed the contract.</p>

<h2>Q1 2019 and beyond</h2>

<p>And now, now we're here. My contract runs until August 2019 and I have a month of leave in May due to my getting 
married and moving my fiancée to Austria. My pay is still on a slight upward trajectory and I do not know where it will go next.</p>

<p>All I know is that right now I have leads approaching me from every side and that I don't really have the time and will
to take them on. Starting with the new year I also found some more energy to invest in my side projects. In the first
9 days I've already started this blog, <a href="https://howmuchdoesthismeetingco.st">How Much Does This Meeting Cost?</a> and a
little <a href="https://github.com/pretzelhands/jinx">nginx wrapper called jinx</a>.</p>

<p>Things are good right now. Things have been good for a long while now. And I stand by one single statement: Throughout
most of this journey I had little to no clue as to what I was doing. I lucked out. I really, really had an absurd amount
of luck when I needed it the most. And sometimes, a little luck goes a long way.</p>

<p>If you find yourself having more questions, <a href="https://twitter.com/_pretzelhands">please do reach out to me on Twitter!</a>
Or maybe <a href="https://t.me/pretzelhands">on Telegram!</a></p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Wed, 09 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Releasing jinx - a magical wrapper around nginx]]></title>
        <link>https://pretzelhands.com/posts/jinx</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/jinx</guid>
        <description><![CDATA[Find out about this new streamlined tool to help you get your nginx workflows done more quickly.]]></description>
        <content:encoded><![CDATA[<p><x-image
    src="jinx-logo-dev-to.png"
    alt="jinx - a magical nginx wrapper"
/></p>

<p>I am super excited about today's blog post because I made a thing! Yes, I actually made a thing with my own two hands
and now I'm releasing it! I present to you: <strong>jinx</strong></p>

<p style="background-color: #f1f1f1; padding: 1.5rem; margin: 2rem auto; text-align: center">
👉 You can <a href="https://github.com/pretzelhands/jinx">find it on GitHub.</a> Go ahead and star it. I'll wait.
</p>

<p>Done? Great. Now as you may remember, I very recently <a href="/posts/bringing-your-entire-infrastructure-down">killed my entire server infrastructure</a> 
by accidentally generating way too many Let's Encrypt accounts in a very short timeframe. It was super funny and I spent 
the better part of my Saturday getting everything back up on a backup nginx setup.</p>

<p>As part of that I quickly noticed a very repetitive pattern to setting up my virtual hosts. It went a little something
like this for every site</p>

<pre><code class="language-bash"># create a new site
cp \
    /etc/nginx/configurations/php.conf \
    /etc/nginx/sites-available/pretzelhands.com.conf

# change the host name to the new site name
nano /etc/nginx/sites-available/pretzelhands.com.conf  

# activate the site in nginx
ln -s \
    /etc/nginx/sites-available/pretzelhands.com.conf \
    /etc/nginx/sites-enabled

# restart to publish newly activated sites
systemctl restart nginx
</code></pre>

<div class="remark">
<p>Just typing out this code block made me feel gross</p>
</div>

<p>Using <code>^R</code> was a bit faster but still incredibly tedious. That's 4 different commands and I have to enter the same or
a similar path a whopping four times. That just didn't feel good and so I remembered how much I wrote about shell-scripting
recently and decided to make use of it to create <strong>jinx</strong>.</p>

<p>With it this same process now looks like this.</p>

<pre><code class="language-bash"># create a new site and insert hostname in template file
jinx site create pretzelhands.com php

# activate the site and restart
jinx site activate pretzelhands.com -r
</code></pre>

<div class="remark">
<p>It's so beautiful I might just cry</p>
</div>

<p>From four long, repetitive commands to two quite short ones. What a nice improvement. And it only cost me my Sunday afternoon.</p>

<p>The initial development effort may have been higher than setting the sites up manually but I'm still glad I did it because
it was fun to do and I can reuse it all the time. Every time I create a new virtual host, this tool probably saves me some
2-3 minutes of repetitive typing.</p>

<p>There's also a few other features, but you can <a href="https://github.com/pretzelhands/jinx">read up on everything in the documentation</a>.
I freely admit that this is probably my single most useful side-project to date. Also it got the approval of <a href="https://twitter.com/levelsio">Pieter Levels</a>
so I guess that counts for something!</p>

<p style="background-color: #f1f1f1; padding: 1.5rem; margin: 2rem auto; border: 1px solid #ccc; text-align: center">
👉 Don't forget to  <a href="https://github.com/pretzelhands/jinx">star it on GitHub.</a>
</p>

<p>I hope you get as much joy and and practical use out of this tool as I do and I'll be trying to extend it with some more
useful features such as activating HTTPS and building configurations from included snippets. We'll see how far I can take
it!</p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Mon, 07 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Bringing your entire infrastructure down using Caddy]]></title>
        <link>https://pretzelhands.com/posts/bringing-your-entire-infrastructure-down</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/bringing-your-entire-infrastructure-down</guid>
        <description><![CDATA[Read my cautionary tale about accidentally crashing my entire server using Let's Encrypt and Caddy.]]></description>
        <content:encoded><![CDATA[<p>If you've talked to me about server infrastructure and DevOps before, there's a reasonable chance that I have told you
about <a href="https://caddyserver.com/">our lord and saviour Caddy</a>. If you're not aware about it, it's an HTTP/2 web server
written in Go that has super simple configuration and automatic HTTPS certificates. Neat, right? Right.</p>

<p><strong>Yesterday Caddy successfully crashed all websites running on my server thanks to automatic HTTPS certificates</strong></p>

<h2>Setting the stage</h2>

<p>Yesterday I was moving my blog from blechi.at to pretzelhands.com - The reason being that the shared host that runs behind
blechi.at (which is a domain I share with my dad), seems to block requests from various Asian countries. I don't know why
it happens and I can't be assed to ask support about it.</p>

<h2>Kill your infrastructure in 60 seconds</h2>

<p>Caddy requests its certificates from <a href="https://letsencrypt.org">Let's Encrypt</a>, as everyone should be doing in 
<code>$CURRENT_YEAR</code>. So while I was happily rearranging my config for pretzelhands.com, I restarted my server multiple times. 
Apparently I had setup my service for Caddy wrong however, causing it to register another account with Let's Encrypt 
every time I restarted the server. Usually this isn't a problem, because I restart my server maybe once or twice. 
I was fiddling around a lot yesterday, however and kept on requesting more and more accounts.</p>

<p>Turns out that <a href="https://letsencrypt.org/docs/rate-limits/">Let's Encrypt really don't like that.</a></p>

<p>Before I knew I hit a rate-limit and my entire server just came crashing down in a magical rainbow of red error messages.
According to Let's Encrypt this rate limit for accounts applies to 10 accounts per 3 hours. I have been trying to wait the
specified amount of time and request a fresh set of certificates. So far I haven't been successful. Maybe they're also
pissy about me requesting multiple certificates for the same domain. I simply don't know.</p>

<p>So I did the next best thing and pulled up nginx, which was stilled installed on my server anyways. And now here we are.</p>

<h2>How to prevent this</h2>

<p>One thing I learned about very quickly yesterday is that Caddy offers a <code>-ca</code> flag with which you can point it to the 
staging environment of Let's Encrypt. That is what I should have done. I've already implented a little script and called
it <code>caddy-test</code>. It will do just that.</p>

<p>I still love Caddy with all of my head, but this was a real shotgun to the foot moment. Woops. Now excuse me while I bring
the rest of my infrastructure back up. Grmbl.</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Sat, 05 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Parsing command-line arguments and flags]]></title>
        <link>https://pretzelhands.com/posts/command-line-flags</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/command-line-flags</guid>
        <description><![CDATA[Learn how to parse flags in your command-line script in a beautiful way. ]]></description>
        <content:encoded><![CDATA[<p>Today's post is going to be a bit more complex if you're new to shell scripting, but something I found quite beautiful 
is how one can go about parsing command-line arguments and flags in shell scripting. It works by using a switch-case 
statement and the <code>shift</code> expression.</p>

<p>Let's take a look!</p>

<pre><code class="language-bash"># arguments.sh

# Default values of arguments
SHOULD_INITIALIZE=0
CACHE_DIRECTORY="/etc/cache"
ROOT_DIRECTORY="/etc/projects"
OTHER_ARGUMENTS=()

# Loop through arguments and process them
for arg in "$@"
do
    case $arg in
        -i|--initialize)
        SHOULD_INITIALIZE=1
        shift # Remove --initialize from processing
        ;;
        -c=*|--cache=*)
        CACHE_DIRECTORY="${arg#*=}"
        shift # Remove --cache= from processing
        ;;
        -r|--root)
        ROOT_DIRECTORY="$2"
        shift # Remove argument name from processing
        shift # Remove argument value from processing
        ;;
        *)
        OTHER_ARGUMENTS+=("$1")
        shift # Remove generic argument from processing
        ;;
    esac
done

echo "# Should initialize: $SHOULD_INITIALIZE"
echo "# Cache directory: $CACHE_DIRECTORY"
echo "# Root directory: $ROOT_DIRECTORY"
echo "# Other arguments: ${OTHER_ARGUMENTS[*]}"
</code></pre>

<div class="remark">
<p>Code like this is why I'm in a love-hate relationship with my terminal</p>
</div>

<p>Phew. That looks like a whole bunch of code. It includes all process of catching command line arguments. But let's go
through everything bit by bit. First, let's start with the default values.</p>

<h3>Defining default values</h3>

<pre><code class="language-bash"># Default values of arguments
SHOULD_INITIALIZE=0
CACHE_DIRECTORY="/etc/cache"
ROOT_DIRECTORY="/etc/projects"
OTHER_ARGUMENTS=()
</code></pre>

<div class="remark">
<p>You can also make the default values empty strings! Just use what makes sense to you</p>
</div>

<p>This is simple enough. If the user doesn't pass in a certain argument, we fill it with some default value we're happy with.
Alternatively you can make the strings empty and check if these empty values are still there. In this way you can easily
verify that you have all necessary arguments passed in. How you go about that is an implementation detail of your script
and thus left as an exercise for the reader. I recommend <a href="https://www.tldp.org/LDP/abs/html/comparison-ops.html">tldp.org for learning about operators</a>.</p>

<p>Style-wise I like defining my arguments in all-caps <code>snake_case</code>, because I generally treat them as constants that I do
not modify. You may disagree and you're welcome to call them however you like.</p>

<h3>Looping through arguments</h3>

<pre><code class="language-bash">for arg in "$@"
do
  .. SNIP ..
done
</code></pre>

<div class="remark">
<p>Funnily enough for loops end with done instead of rof. Consistency!</p>
</div>

<p>Looping through the arguments is equally simple. You simply loop over the magic <code>$@</code> variable your shell provides to you.
It contains an array of the exact command as it was called, starting after the file name.</p>

<p>So if you call your script using <code>./arguments.sh -i --cache=/var/cache --root /var/www/html/public my-project</code>, 
then the array will look a bit like so</p>

<pre><code class="language-html">(
   $0 = ./arguments.sh
   $1 = -i
   $2 = --cache=/var/cache
   $3 = --root
   $4 = /var/www/html/public
   $5 = my-project
)
</code></pre>

<div class="remark">
<p>This is not the exact notation of arrays in shell, but this will be important in a second</p>
</div>

<p>Note that the <code>$@</code> variable <strong>does not</strong> contain the value of <code>$0</code>. If you however access <code>$0</code> normally, it will
return the file name you used to call the script.</p>

<p>For our purposes we loop over each entry in the array and put it in a temporary <code>$arg</code> variable.
Now we can process the arguments.</p>

<p><x-newsletter /></p>

<h3>Processing all arguments</h3>

<p>The arguments will be processed in a switch-case statement. As you may have noticed in the full code sample above, those
come with their own delightful idiosyncrasies in syntax. <a href="https://pretzelhands.com/posts/non-obvious-behaviors">Like a lot of other things in shell scripting, really.</a>
A case statement looks like this:</p>

<pre><code class="language-bash">    case $arg in
        .. SNIP ..
    esac
</code></pre>

<div class="remark">
<p>The $arg variable in this case is the one we declared in the for-loop above</p>
</div>

<p>Now let's look at the various ways to process arguments and how to write switch cases.</p>

<h4>Boolean flags</h4>

<p>Boolean flags are those which may be there or not. A good example might be a <code>--help</code> flag. Parsing those looks like so</p>

<pre><code class="language-bash">-i|--initialize)
SHOULD_INITIALIZE=1
shift # Remove --initialize from processing
;;
</code></pre>

<div class="remark">
<p>Note the two semicolons. Yes, you need those. Both of those.</p>
</div>

<p>This case statement checks whether the current value of <code>$arg</code> is either <code>-i</code> or <code>--initialize</code>. In our case this is true
and thus we set the <code>SHOULD_INITIALIZE</code> variable to <code>1</code> to indicate that the flag is present. Afterwards we pop the value
<code>$arg</code> off of our <code>$@</code> array using <code>shift</code>. It now looks like the following:</p>

<pre><code class="language-html">(
   $0 = ./arguments.sh
   $1 = --cache=/var/cache
   $2 = --root
   $3 = /var/www/html/public
   $4 = my-project
)
</code></pre>

<div class="remark">
<p>Note that the value of $0 stayed the same while everything else shifted up by one.</p>
</div>

<h4>Equals-separated flags</h4>

<p>Our next case statement parses command-line flag of the form <code>--arg=value</code>, which is the traditional style of passing arguments.
You can often see this when using Unix tools such as <code>ls --color=auto</code>.</p>

<pre><code class="language-bash">-c=*|--cache=*)
CACHE_DIRECTORY="${arg#*=}"
shift # Remove --cache= from processing
;;
</code></pre>

<div class="remark">
<p>This is where you realize that shell scripting has magical features</p>
</div>

<p>In this case we check if the current <code>$arg</code> matches the either <code>-c=</code> or <code>--cache=</code> followed by any number of characters.
If it does we take that <code>arg</code> variable into our string and remove the parts of it we don't need. The <code>#*=</code> part looks super
confusing at first. What it does is remove everything character from the beginning of <code>$arg</code> until it finds an equals sign.</p>

<p>This means that <code>--cache=/var/cache</code> becomes <code>/var/cache</code>. If you want to read up more on the topic of parameter substitution
in shell scripts, I recommend <a href="https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html">this article from cyberciti.biz</a></p>

<p>After this our <code>$@</code> array of arguments now looks as follows:</p>

<pre><code class="language-html">(
   $0 = ./arguments.sh
   $1 = --root
   $2 = /var/www/html/public
   $3 = my-project
)
</code></pre>

<h4>Space-separated flags</h4>

<p>Our third case statement handles command-line flags of the form <code>--arg value</code>, which is a more modern approach.
You can usually see it with command-line tools written with Node.js or Python.</p>

<pre><code class="language-bash">-r|--root)
ROOT_DIRECTORY="$2"
shift # Remove argument name from processing
shift # Remove argument value from processing
;;
</code></pre>

<div class="remark">
<p>At this point these are probably a breeze to go through</p>
</div>

<p>Compared to the previous handler, this one is again rather easy to understand. We check whether <code>$arg</code> is equal to <code>-r</code>
or <code>root</code> then we take the value of <code>$2</code> into our <code>ROOT_DIRECTORY</code> variable and <code>shift</code> twice.</p>

<p>Why do we take <code>$2</code>? Remember: We have shifted away all previous arguments passed to the script so that now <code>$1</code> is equal
to the value of <code>$arg</code> and thus <code>$2</code> now contains the arguments value.</p>

<p>After we shift the next two values off, we remain with this arguments array</p>

<pre><code class="language-html">(
   $0 = ./arguments.sh
   $1 = my-project
)
</code></pre>

<div class="remark">
<p>Just one more step to go and we're done</p>
</div>

<p>As the last step we will handle all the other arguments passed in without a flag. Let's go!</p>

<h4>Matching other arguments</h4>

<p>Our final case matches any value that wasn't matched by our previous handlers. These can be arguments passed without
any flag, like a project name, or something else entirely.</p>

<pre><code class="language-bash">*)
OTHER_ARGUMENTS+=("$1")
shift # Remove generic argument from processing
;;
</code></pre>

<div class="remark">
<p>"Pop!" goes the weasel and adds the value to an array</p>
</div>

<p>For this handler we simply take the value of <code>$1</code> and add it to a miscellaneous array. After all the additional arguments
have been added to the array, you can decide to do whatever you like. For example the first entry in the array could be
a project name. Who knows!</p>

<h3>Trying it all out</h3>

<p>Now if you add some echo statements and  try to run your script as stated above with 
<code>./arguments.sh -i --cache=/var/cache --root /var/www/html/public my-project</code> you could see output like the following</p>

<pre><code class="language-html">$ ./arguments.sh -i --cache=/var/cache --root /var/www/html/public my-project

# Should initialize: 1
# Cache directory: /var/cache
# Root directory: /var/www/html/public
# Other arguments: my-project
</code></pre>

<h2>Closing thoughts</h2>

<p>I think that the use of such a switch-case statement together with some more advanced features of shell scripting makes
for a really nice and extendable way to add command-line arguments and flags to your scripts. It also allows for great
flexibility, so if you don't like being stuck with one style you can easily use the other.</p>

<p>Enjoy!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Fri, 04 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Non-obvious behaviors of shell syntax]]></title>
        <link>https://pretzelhands.com/posts/non-obvious-behaviors</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/non-obvious-behaviors</guid>
        <description><![CDATA[Learn about the pitfalls and tricks behind some of the unusual shell syntax.]]></description>
        <content:encoded><![CDATA[<p>When it comes to shell scripting there are some odd quirks and behaviours to the syntax that may not be immediately obvious.
This post aims to collect my learnings of general shell weirdness, and maybe it will help you as well!</p>

<h2>Escaping</h2>

<p>When using a variable, you will always want to use double quotes when getting it's value. This is because otherwise your
terminal of choice might decide to interpret a number of characters in it's own, delightfully destructive way.</p>

<p>An easy example can be given with looping over a variable's contents without using quotes.</p>

<pre><code class="language-bash">list="one two three"

for word in $list
do
    echo "$word"
done

# Contrary to what you might expect this will output
# three variables in sequence:

## =&gt; one
## =&gt; two
## =&gt; three
</code></pre>

<div class="remark">
<p>And just like that you're in for a nasty surprise</p>
</div>

<p>Woops! Looks like your terminal just turned your nice string into a whitespace-delimited array. Which is clearly what you
wanted all along, no?</p>

<p>To prevent this, you need only wrap the <code>$list</code> variable in quotes, like so</p>

<pre><code class="language-bash">list="one two three"

for word in "$list"
do
    echo "$word"
done

# This will now output the expected string

## =&gt; one two three
</code></pre>

<div class="remark">
<p>Quotes are magic. Never forget that</p>
</div>

<p>So as an easy rule to remember: Unless you're on a treasure hunt for pain, just <em>always</em> wrap your variables in quotes.</p>

<h2>Conditionals</h2>

<p>Conditionals in shell scripting is super fun, because wrapping things in various bracket formats is totally what you expect to do.
But those brackets hold deep dark mysteries that can easily trip you up when you want to do simple conditionals.</p>

<p>Let's do a simple number comparison. For our intents and purposes let's agree that  <code>10</code> is bigger than <code>5</code>.
Now consider the following example</p>

<pre><code class="language-bash"># conditionals.sh

ten=10
five=5

if [ "$ten" &gt; "$five" ]
then
        echo "ten is bigger than five"
else
        echo "ten is smaller than five"
fi

</code></pre>

<div class="remark">
<p>I'll let you guess what the outcome will be</p>
</div>

<p>The outcome should be quite obvious, no? Run the script and we should see <code>ten is bigger than five</code>. Easy!</p>

<p>And that's where you're wrong. Here's what the actual output is.</p>

<pre><code class="language-html">./conditionals.sh: line 6: 10: No such file or directory
ten is smaller than five
</code></pre>

<div class="remark">
<p>It's like some kind of alternate reality. Spooky</p>
</div>

<p>Wait, what? We're getting a file not found error and according to the shell <code>10</code> is smaller than <code>5</code>. I don't know what
math class you took, but in my world that's just all kinds of wrong.</p>

<p><strong>What happened?</strong></p>

<p>The first thing that went wrong is that we used a single pair of brackets. When we do that, the shell will automatically
try to execute and compare to a file glob pattern and trips up because it can't find a directory named <code>&gt;</code>. The easy way
to fix this is either to use double braces like so: <code>[[ "$ten" &gt; "$five" ]]</code> or just escape the greater-than symbol
with a backward slash like so <code>\&gt;</code>.</p>

<p>This will get rid of the first error, however you will still receive output telling you that <code>ten is smaller than five</code>.
The problem here is that by using square brackets, the shell will automatically do a lexical comparison. And in the alphabet one
and zero generally come before five.</p>

<p>Now there's two ways we can correct this, and I'll show you both.</p>

<pre><code class="language-bash"># conditionals.sh

ten=10
five=5

# Solution 1
# This version uses the -gt switch to force
# arithmetic comparison.
if [ "$ten" -gt "$five" ]
then
        echo "ten is bigger than five"
else
        echo "ten is smaller than five"
fi

# Solution 2
# This version uses a pair of parentheses (( )) to force 
# arithmetic comparison.
if (( "$ten" &gt; "$five" ))
then
        echo "ten is bigger than five"
else
        echo "ten is smaller than five"
fi
</code></pre>

<div class="remark">
<p>It just all makes sense. All of it</p>
</div>

<p>There are lots of other different comparison operators like <code>-gt</code> and you can find an extensive guide over at
<a href="https://www.tldp.org/LDP/abs/html/comparison-ops.html">tldp.org</a> along with the exact comparison syntax.</p>

<h2>Functions</h2>

<p>For the last chapter of this wild joy ride through shell behavior, we will take a look at functions. They too behave in
ways that might be confusing if you come from other programming languages.</p>

<h3>Calling a function</h3>

<p>Contrary to basically every other programming language out there (save for some exceptions), bash does not use parentheses
for a function call. You simply write the name. If you want to pass in some arguments, just add them with a space after
the function call. It looks like this:</p>

<pre><code class="language-bash">function foobar {
    echo "Foo to the bar"
}

foobar
foobar "argument1" "argument2"
</code></pre>

<div class="remark">
<p>This feels so.. empty</p>
</div>

<p>Both calls will do the exact same thing. One thing to note is that you can pass any number of arguments to a shell function
and it will not care. If you don't use arguments, they will simply be ignored. Another thing to be aware of is that you
do not need to write parentheses after the function name. Your terminal simply doesn't care.</p>

<h3>Using arguments</h3>

<p>Unlike other programming languages, arguments in shell scripting are not defined in the function signature, but are rather
used by using special <code>$</code> variables, like so.</p>

<pre><code class="language-bash">function foobar {
        argument_1=$1
        argument_2=$2
        argument_list=$@

        echo "First argument: $argument_1"
        echo "Second argument $argument_2"
        echo "List of arguments: $argument_list"
}

foobar "argument1" "argument2"

# Generated output
## =&gt; First argument: argument1
## =&gt; Second argument argument2
## =&gt; List of arguments: argument1 argument2
</code></pre>

<div class="remark">
<p>I think this is where Larry Wall got inspired for Perl</p>
</div>

<p>This also means that it's completely up to you to check whether an argument is actually set and throw an error if that
is not the case. If you want to output nice error messages I highly recommend 
<a href="/posts/coloring-terminal-text">my post on coloring your terminal output.</a> Ahem.</p>

<h3>Variable scope</h3>

<p>Another fun thing about shell scripts. If you define variables in your function like I did above, they are by default
in global scope and can be accessed by anyone after the first time the function was called. Check this out</p>

<pre><code class="language-bash">function foobar {
        argument_1=$1
        argument_2=$2
        argument_list=$@

        echo "First argument: $argument_1"
        echo "Second argument $argument_2"
        echo "List of arguments: $argument_list"
}

foobar "argument1" "argument2"
echo "Outside the function: $argument_1"

# Generated output
## =&gt; First argument: argument1
## =&gt; Second argument argument2
## =&gt; List of arguments: argument1 argument2
## =&gt; Outside the function: argument1
</code></pre>

<div class="remark">
<p>A whole new level of "Hello World"</p>
</div>

<p>To prevent this behavior, you simply need to prefix your variables with the <code>local</code> keyword to prevent the function scope
from spilling into the outside world. It looks like so</p>

<pre><code class="language-bash">function foobar {
        local argument_1=$1
        local argument_2=$2
        local argument_list=$@

        echo "First argument: $argument_1"
        echo "Second argument $argument_2"
        echo "List of arguments: $argument_list"
}

foobar "argument1" "argument2"
echo "Outside the function: $argument_1"

# Generated output
## =&gt; First argument: argument1
## =&gt; Second argument argument2
## =&gt; List of arguments: argument1 argument2
## =&gt; Outside the function: &lt;nothing&gt;
</code></pre>

<div class="remark">
<p>Now the variable is contained as it should be</p>
</div>

<p>And as grand finale, let's talk about return values!</p>

<h3>Returning values</h3>

<p>You'd think that using a simple <code>return</code> statement would be enough, yes? I hate to break it to you, but no. No, of course
not. Because doing so would be entirely too easy. Instead what you do is to <code>echo</code> your return value and then capture the
output into a new variable. This looks somewhat like this</p>

<pre><code class="language-bash">function foobar {
        local argument_1=$1
        local argument_2=$2
        local argument_3=$3

        echo "$argument_1 $argument_2 $argument_3"
}

foobar_returned=$(foobar "foo" "bar" "baz")
echo "Foobar return value: $foobar_returned"

# Generated output
## =&gt; Foobar return value: foo bar baz
</code></pre>

<div class="remark">
<p>If you've gotten this far without giving up, I applaud you</p>
</div>

<p>Phew. Makes you feel like somebody repairing their boat by putting duct tape over all the leaky holes.</p>

<h2>Final thoughts</h2>

<p>As you can see the world of shell scripting has lots of traps for unsuspecting people strolling past. I hope this post
helps you avoid some of these traps in your own scripts. For a concise reference on most shell syntax you can check out
<a href="https://learnxinyminutes.com/docs/bash/">learnxinyminutes.com</a>. It's my go-to reference for odd quirks and things I just
cannot seem to remember.</p>

<p>Enjoy, and good luck out there!~</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Thu, 03 Jan 2019 09:10:21 +0000</pubDate>

            </item>
        <item>
        <title><![CDATA[Coloring text in your terminal]]></title>
        <link>https://pretzelhands.com/posts/coloring-terminal-text</link>
        <guid isPermaLink="true">https://pretzelhands.com/posts/coloring-terminal-text</guid>
        <description><![CDATA[Learn how to color your output with native shell scripts to give better feedback to your users.]]></description>
        <content:encoded><![CDATA[<p>Recently I've been writing lots of Bash scripts at work to automate a whole array of tasks from setting up a cluster of
Docker containers to setting up project structures and so on. You could say I'm on a bit of an automation rush.</p>

<p>Now, I remembered of course that there's possibilities to color your text in the terminal and npm for example has <a href="https://www.npmjs.com/package/chalk">a 
wonderful package called chalk</a> to do this. But in the environment I'm dealing with
there is no npm and frankly Bash just feels like a more direct way of writing automation scripts, so I figured that there
must be an easy way to do this.</p>

<p><x-image
    src="color-bash-01.png"
    alt="A demonstration of color in the terminal"
    caption="A comparison of uncolored text against fancy colored one"
/></p>

<p>And of course there is. All you have to do is use some escape sequences and a flag when using <code>echo</code>. And that's really
all there is to it. First, let's define the available color codes as variables.</p>

<pre><code class="language-bash">COLOR_RED="\033[0;31m"
COLOR_RED_LIGHT="\033[1;31m"

COLOR_GREEN="\033[0;32m"
COLOR_GREEN_LIGHT="\033[1;32m"

COLOR_ORANGE="\033[0;33m"
COLOR_YELLOW="\033[1;33m"

COLOR_BLUE="\033[0;34m"
COLOR_BLUE_LIGHT="\033[1;34m"

COLOR_PURPLE="\033[0;35m"
COLOR_PURPLE_LIGHT="\033[1;35m"

COLOR_CYAN="\033[0;36m"
COLOR_CYAN_LIGHT="\033[1;36m"

COLOR_GRAY="\033[1;30m"
COLOR_GRAY_LIGHT="\033[0;37m"

COLOR_BLACK="\033[0;30m"
COLOR_WHITE="\033[1;37m"

COLOR_END="\033[0m"
</code></pre>

<div class="remark">
<p>These are the terrible escape sequences corresponding to colors</p>
</div>

<p>These are the 16 most commonly supported colors for various terminal emulators. Feel free to mix and match as you require
them. The reason why we define these as variables is so that we can use them for formatting more easily. I won't stop
you from remembering all the escape sequences, though!</p>

<p>Now that we have all the color codes defined we can do something like the command below.</p>

<pre><code class="language-bash">echo -e "
    ${COLOR_GREEN}Success!${COLOR_END} 
    The task was executed successfully!
"
</code></pre>

<div class="remark">
<p>Using this you can go forth and create your own rainbow</p>
</div>

<p>The things to note here are as follows: First, whenever you want to style your string with colors, you <strong>must</strong> add the 
<code>-e</code> flag to <code>echo</code>, otherwise you'll just end up with a garbled mess in your terminal. Second, whenever you are finished
with using a color be sure to close it with a <code>${COLOR_END}</code> tag. Otherwise the color will keep going on and on.</p>

<p><x-image
    src="color-bash-02.png"
    alt="Another demonstration of color in the terminal"
    caption="The result of above command. Whitespace is for aesthetic reasons. You do your own"
/></p>

<p>There are many more formatting options available in the terminal. To get an overview of all of them I recommend you
<a href="https://misc.flogisoft.com/bash/tip_colors_and_formatting">visit FLOZz' wiki page</a> on the subject.</p>

<p>Enjoy!</p>
]]></content:encoded>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Blechinger (pretzelhands)</dc:creator>
        <pubDate>Wed, 02 Jan 2019 09:10:21 +0000</pubDate>

            </item>
    </channel>
</rss>
