Emoji short name’s to HTML HEX Code via PHP

I've been working on a Slack App that shows recent Slack posts and activities. The majority of recent posts include emojis, and problematically Slack's API doesn't store and send emojis as characters, they're down-converted to their respective shortnames, then wrapped in colons (presumably for universal support - it appears to be a standard practice of some sort, as many use it).

Basically I was just looking for converting

:thumbsup: to convert to: \👍 which'd show: đź‘Ť

Simple enough task, but I couldn't find any resources to do it (and if you've discovered this blog post, you've probably discovered that out too). So, starting at the top, Slack in their API docs refers to using github.com/iamcal/emoji-data as their emoji shortcode source. This repository does have a PHP script, but it doesn't convert as simply as I wanted it to, and it's huge.

Taking a step back, iamcal/emoji-data has a enormous master .json file, basically a database of each emoji and all variations therein. Taking this file, and running through it with some basic PHP, we can build a simple array of shortcode => hexcode. Doing this was almost too easy:

$table = json_decode(file_get_contents('emoji.json'));
//print_r($table); //take a peak
foreach ($table as $k => $emoji) 
  foreach ($emoji->short_names as $shortname)
    echo "'{$shortname}' => '{$emoji->unified}', \n";

We then take the output of that file, and create our own emoji_unicode.php file, an array housing the values output above. Which looks like:

$emoji_unicode = array(
'copyright' => '00A9',
'registered' => '00AE',
'bangbang' => '203C',
...etc.

Much smaller and easier to work with. 97% smaller actually coming in at: 35KB instead of 928KB. I've hosted the file on github here.

So with a simple array to call back to, we can just include it, search the string for the colon'ized shortcode emojis and replace with hex code, like so:

require('emoji_unicode.php');

$text = "hello :world_map: . it's :hammer: :stopwatch:.";

//alphanumeric, hyphens, plus-signs, and underscores string, wrapped in colons
preg_match_all("/:([a-zA-Z0-9'_+-]+):/", $text, $emojis);

foreach ($emojis[1] as $emojiname)
  $text = str_replace(":". $emojiname.":", "&#x".$emoji_unicode[$emojiname].";", $text);

echo $text;

Which'd output

hello 🌎 . it's 🔨 ⏱.

This works with :skin-tone-x which surprised me- I guess the skin-tone is a "invisible" character of it's own, that basically gets compounded with the emoji character before it, and changes the emojis that way. Pretty cool, so no extra work for that. (And no, this method doesn't work on devices that don't support emojis).

goodreads_widget

Kindle Currently Reading on Panic Status Board or Website

Here's a script that'll fetch your currently-reading and read books from your Goodreads shelf, and show them like a widget for either Panic Status Board (DIY Panel), or your website, or wherever else you want it. Personally, I don't like the lack of book-covers on my Kindle Paperwhite, so I enjoy seeing it on my propriety dashboard/statusboard, adds a personal touch to all the graphs, charts, and numbers.

Code is inspired and built upon from a great little 2013 script from flapane.com

Setup:

<?
// code "inspired" from flapane.com/blog/en/2013/03/creare-un-widget-per-goodreads-con-gli-ultimi-libri-letti/ - instead: used CURL, relogic cache, output different.
// settings
$apikey = "#####################"; //via goodreads.com/api/keys
$user = "########-your-username"; //goodreads.com > View My Profile, last chunck of URL
$refreshbook = 24; //hours
$cache = __DIR__."/goodreads.cache"; //make this file manually in same DIR as this file
$force_refresh = false; //dev
$breaker = "@@@@@@@@@@@@@@@@@@@@@@@@"; // something to separate two chuncks of json
$image_fix = array( //stupid when covers missing from api but not Goodreads.com
 'id_#########' => 'https://d.gr-assets.com/.....jpg',
);
$curl_options = array(
  CURLOPT_RETURNTRANSFER    => true,
  CURLOPT_HEADER            => false,
  CURLOPT_FOLLOWLOCATION    => false,
  CURLOPT_ENCODING      => "",
  CURLOPT_AUTOREFERER   => true,
  CURLOPT_CONNECTTIMEOUT    => 8,
  CURLOPT_TIMEOUT       => 8,
  CURLOPT_MAXREDIRS         => 3,
  CURLOPT_SSL_VERIFYHOST    => false,
  CURLOPT_USERAGENT     => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.A.B.C Safari/525.13"
);          
?>

Query and cache:

<?
// Is the file older than $refreshbook, or the file is new/empty?
if ($force_refresh || ((time() - filectime($cache)) > ($refreshbook * 3600) || 0 == filesize($cache))) {

    // the query, pretty straight forward
    $reading_books_path = "https://www.goodreads.com/review/list/".$user.".xml?key=".$apikey."&v=2&shelf=currently-reading&sort=date_started&order=d&page=1&per_page=2";

    // make the curl request, basically get_file_contents().
    $ch = curl_init($reading_books_path);
    curl_setopt_array($ch, $curl_options);
    $content = curl_exec( $ch );
    if(curl_error($ch)) output('Curl error:' . curl_error($ch) . "<br />");
    curl_close( $ch );

    # again, but for read
    sleep(2);//1 query per second for goodreads api
    $reading_books_path = "https://www.goodreads.com/review/list/".$user.".xml?key=".$apikey."&v=2&shelf=read&sort=date_started&order=d&page=1&per_page=5"; 
    $ch = curl_init($reading_books_path);
    curl_setopt_array($ch, $curl_options);
    $content_read = curl_exec( $ch );
    if(curl_error($ch)) output('Curl error:' . curl_error($ch) . "<br />");
    curl_close( $ch );

    // save curl request results to cache, store together, but serperate two
    $handle = fopen($cache, 'wb');  
    fwrite($handle, $content.$breaker.$content_read);
    fclose($handle);
}
?>

Read and output:

<?
// read cache and convert XML to ARRAY
$data_cache = explode($breaker,file_get_contents($cache));
$reading    = new SimpleXMLElement($data_cache[0]);
$read       = new SimpleXMLElement($data_cache[1]);

echo "<div id='bookshelf'>";

// cycle through currently reading (@TODO i dont know if cycle works nativily, only developed with 1 book in shelf. sometimes they change the xml structure when >1.  would need to change)
foreach ($reading->reviews->review as $reading)
    echo "<div title='{$reading->book->title}' class='book reading' style='background-image:url(".str_replace('http:', 'https:', $reading->book->image_url).");'></div>";

// cycle through read
$c = 1;
foreach ($read->reviews->review as $read) {
    $img = @(isset($image_fix['id_'.$read->book->id])) ? $image_fix['id_'.$read->book->id] : $read->book->image_url;
    if ($c < 4)
    echo "<div title='{$read->book->title} - #{$read->book->id}' class='book read' style='background-image:url(".str_replace('http:', 'https:', $img).");'></div>";
    $c++;
}

echo "</div><!--/bookshelf-->";
?>

And some styling:

```

<style>
#bookshelf {
    width:156px;
    margin: 0 auto;
}
.book { 
    -webkit-filter: grayscale(1); 
    -webkit-filter: grayscale(100%); 
    filter: grayscale(100%); 
    filter: gray;
    background-size:cover;
    background-position:center center;
    background-repeat: no-repeat;
    width:98px; // original
    height:147px; // original
    width:<?= (98/147)*172 ?>px; // new
    height:172px; // new
    box-shadow: 0 0 3px 3px rgba(0,0,0,0.09);
    display:block;
}
.reading {
    float: left;
    margin-right: 5px;
}
.read {
    float: left;
    width:98px; // original
    height:147px; // original
    width:<?= (98/147)*54 ?>px; // new
    height:54px; // new
    opacity: 0.8;
    margin: 0 0px 5px 0;
}
</style>

You'll need to create empty file goodreads.cache manually, in the dir of the script.

I'm not entirely sure what happens when there's more than 1 currently-reading. I never bothered checking.

If this code gives you trouble, replace the shorthand php tags <?, <?= with <?php, <?php echo

$image_fix is nasty, I don't understand why the Goodreads user API would not return a cover when the cover is present on the Goodreads site itself. I had to do a fix for one of my books, manually tracking down the cover's URL on Goodreads.com.

width:<?= (98/147)*54 ?>px; is my ..interesting.. way of doing proportional scaling on the fly. Old width/height values kept prior to overwrites for reference; makes sizing your widgets books covers to much easier.

filter: grayscale(100%); Keepin' it real with Kindle, grayscale the covers.

404’s Are Server, Not WordPress’s 404

Frustrating problem I've had happen more than once. Your Wordpress site returns a 404 (which you may be relying on), but it shows a HTML (server generated) 404 page, not a Wordpress 404 page.

Solution for apache is simple: let the server know to revert to index.php when the header is 404, set the following in .htaccess

ErrorDocument 404 /index.php

VoilĂ 

its_pink

It’s Pink!

For a week we held onto our ultrasound result containing the answer to if it's a little boy or girl. We gave the sealed envelope to her family, who then gave it to a baker. The baker injected the cupcakes with either blue or pink icing.

We all got together, had a really nice day, and here's the heart warming seconds when Jess and I found out that our little baby is going to be a girl (we were convinced it was a boy, the cupcakes even seemed to have a hue of blue to them). With a mouth full of cupcake, she cries out "It's pink! It's Pink" then begins to turn to mush for 15minutes.

yuck

Amazon Kindle PaperWhite’s Screensaver – Concept Solution

I love my Kindle PW 3 (FW: 5.7.4.1), but I hate the screensaver, it's like Motel art. Thrift store art. It's terrible and only belongs on napkins at the dollar store. I would Jailbreak it, but the current FW version has made it pretty much impossible without soldering things.

So what's left to do? I can continue just placing it face-down on my nightstand, or, come up with a concept design and try to get someone at Amazon to read it!

I understand the non-native support for managing screensaver locally: say if a folder /screensavers/ was accessible via USB; users would be uploading bad resolutions, huge raw 12mb files, etc. and the cropping and grayscaling would be unpredictable and unsatisfactory. Any local image editor, would not likely work given the Kindles graphic processing. So, that’d be a nightmare, hundreds of support calls. It'd be far worse if the Kindles Browser had to be used for screensavers; even more problems.

However, Amazon has an eco system already in place to manage more advance things like this: amazon.com - Account - Manage Your Content and Devices . Here is my concept on how easy it could be to manage custom screensavers (for users who've spent the $20 to opt out of the Ads):

Kindle Custom Screen Savers

Pretty simple implementation, using no technology that's not readily available. Set screensaver pushed to the Kindle just like a device name-change or a new book purchased via a computer!

Users would upload normal coloured images, of whatever resolution, then an HTML5/JS image editor would auto black-and-whites, and crops for the proper aspect ratio. Taking things a step further, since converting to grayscale is never what one expects: make things a bit more customizable with brightness and contrast controls. This would be okay for novice users to understand (as it's all the same steps Instagram uses, or Facebook profile pictures).

Important: THIS IS A CONCEPT. Not actually a tutorial. I know you're desperate for this to be real: so am I.

Object Buffering Not Working Flush

One of my servers handled object buffering fine, the other wouldn't partake. After hours of searching, and changing php settings, deep in a thread I found a solution for stubborn servers. If the following simple script doesn't work out the gate:

<?
ob_end_flush();
for( $i = 0 ; $i < 10 ; $i++ ) {
    echo $i;
    flush();
    ob_flush();
    sleep(1);
}
?>

Try this instead, from a gentleman named Roger in Stackoverflow :

<?
@ini_set('zlib.output_compression',0);
@ini_set('implicit_flush',1);
@ob_end_clean();
set_time_limit(0);
header( 'Content-type: text/html; charset=utf-8' );
ob_implicit_flush(1);

for($i=0; $i<10; $i++){
    echo $i;

    //this is for the buffer achieve the minimum size in order to flush data
    echo str_repeat(' ',1024*64);

    sleep(1);
}
?>

A nasty work around that works beautifully, and I hope it helps you.

WordPress Get Post IDs of Navigation Menu

Always a pain, finding the actual POST TYPE object of a Navigation Menu. This is the most straight forward solution I found to get the relation array.

$navigation_relation = array();

$navitems = wp_get_nav_menu_items('your-nav');
foreach ($navitems as $navobject)
    $navigation_relation[$navobject->ID] = $navobject->object_id;

print_r($navigation_relation);

Which will return

(
    [menu_id] => object_id
    [menu_id] => object_id
    [menu_id] => object_id
    ..etc
)
davidsword_backtotheforest5_v6-web

Back To The Forest 5

First poster I've done in a few years - a tad calmer and more realistic than previous work maybe a reflection of ones self? Nah, lets not get too deep. Shown without text. Couldn't of gotten far without the puppet warp tool.

Images used to create collage:

images used

The Best Restaurant Style Bread Machine Bread

I often get caught up in something, for a week at a time. Maybe fixing my truck, a certain book, consuming news, a new gadget, coding something, and far too often binge watching a TV series (most recent: Vikings). This week's obsession: our new bread machine, a christmas present.

I've been obsessed with trying to make restaurant style bread at home- you know, the kind at those Italian joints that they hand out before your meal, to make you think your meal was super filling. At very least, just trying to make bread comparable to the higher-end bread at the store that rings up at over $5.

I tried each recipe the machine came with, and lots of online recipes - but each tasted cheap, gooy, too hard or too soft. I was severely disappointed, and half my compost is now filled with rejected loafs.

Anyways, after much trial-and-error, I've finally made something just right by combining three different recipes. I've created the ultimate best bread machine bread recipe:


Ingredients

  • 1 cup hot water (free!)
  • 2 teaspoons yeast ($0.40)
  • 3 overflowing tablespoons sugar ($0.03)
  • 1/4 cup of butter ($0.62)
  • 1 teaspoon salt ($0.01)
  • 3 cups white flour ($0.30)
  • 1 tablespoon vital wheat gluten ($0.05)
  • 3 tablespoons Italian/vegetable seasoning ($0.35)

Total Cost: $1.76


Directions

  1. Activate yeast in the warm water, 10min; until frothy
  2. Add all other ingredients
  3. Set the machine to 1.5 kg, white/light crust, on the basic setting
  4. Once complete, dump onto cooling rack for 10min

Total Time: 10 minutes to prepare, 3 hours in machine


The vital wheat gluten is the main ingredient, if you don't have it: don't bother. It's the taste difference between cheap stale bread at the ghetto grocery store in town, compared to the soft fluffy I-can't-belive-this-isn't-the-meal restaurant bread you're after.

sc2_ladder_panel_statusboard2

StarCraft 2 Ladder Rank on Panic Status Board

UPDATE: This has not been updated to post 3.4.0 new ladder logic.


In my humble opinion, Panic's Status Board 2 is the best app for the iPad. It's DIY system allows me to write small widgets ("panels") of data, and display it in an elegant and well organized way. I use it all day, every day - I can't work without it. I've made it handle the majority of my notifications, and I'm able to keep an eye on all the important things in my digital life with a glance: server status', project states, calendar events, Slack activity feed, weather, my finances, and now - most importantly - my rank in the StarCraft 2 ladder.

About two years ago, in my StarCraft 2 heyday, I wrote a similar script for Geektools to display SC2 ranking on your desktop - but I never continued development on it. I had used a third party API, which broke - and the setup required a fair bit of coding and know-how on the users end.. overall, it just wasn't a good way of doing things, and stayed in Alpha. A few months later I stopped playing StarCraft all together and lost interest on continue the script. Had to life for a few years.

With my recently renewed interest in StarCraft 2 and the release of Legacy of the Void, I decided to revive the project. This time around, I've coded it with battle.net API directly, and used a much better medium to display it: Status Board. Together this makes it easy to use and setup, and looks much better.

The panel automatically displays a backdrop of your primary race, your username, swarm score, league rank position, win/loss, and win ratio after a simple login to battle.net.. No editing files or trying to find your user ID. 3 or 4 clicks with 1 login; you're ready to go.

Panic will refresh the SC2 panel per its normal panel refresh rate. Each refresh for the SC2 Panel will takes a second or two as it has to first go to Battle.net, verify that you're logged in and that you've approved the Application, then return with authorization and display your data. You can disallow the application from accessing your account by going to battle.net/account/management/authorizations.

How To Get

  1. Open Status Board, edit current board, create a DIY panel (bottom right)
  2. Paste the following in the URL field https://handsomefox.com/sc2/, save changes to board (top left)
  3. Click the "click here" link in the new panel, log into battle.net & approve the access

That's it!

Road Map:

Please note this is an alpha project. There's room for improvement and refinement. Please let me know of any bugs or issues you have.

  • Beta test
  • Smoother refresh process, store output in session so it doesn't momentarily disappear
  • Different Layouts
  • Include Avatar
  • Port for different mediums
  • Post on Github for open source / contribution

If you like this, please consider buying me a beer or leaving a nice comment below. It's my fuel for these side projects.

No more posts.