Finding out sentiment

Say you want to find out if a user generated text is happy, sad, upbeat or hateful. Not so easy is it? We were given this task as a part of a recent project. And to make things even more challenging we needed to do this in both Swedish and English.

After a bit of research I found a very interesting API that pretty much does this – SAPLO. Using their API we can create reference groups of texts, and compare user generated texts with these groups.

Say we create two groups; Positive and Negative.
We then measure each user generated text towards these groups, and get a sentiment back. For instance:

Jag har tråkigt (I’m bored):
[Negative] => 0.14
[Positive] => 0

Fisk är gott (Fish tastes good):
[Negative] => 0.22
[Positive] => 0.28

Många tycker det är tråkigt att resa, men inte jag (Many people things that it’s tedious to travel, but I don’t):
[Negative] => 0.40
[Positive] => 0.46

SAPLO works great on both English and Swedish.

NFC + Krautmilen + Android

kraut

We needed to make something to measure timings during the running event Krautmilen.

Equipment:

  1. Make a fugly Android app which could relay runners NFC keyring blips to a server API.
  2. Make a server API to handle the basic running logic (ie pair up names of runners with their start and finish times)

The application is quite simple: once the NFC tag has been blipped it sends an Intent to the application that contains the NDEF data. We grab the ID from the NDEF and perform a HTTP request to the server API.

Since the Android app basically just relays on sending NFC tag IDs to a server, there are lots of use cases, so we released it on Google Play yey! You can enter your own URL, make HTTP requests and use the data to whatever you want. NFC is everywhere nowdays; in travelcards, passports, library books and much more.

Here’s a small code example for handling the Intent:

private void handleIntent(Intent intent) {
      String action = intent.getAction();

      if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {

            NdefMessage[] messages;
            Parcelable tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            getTagData(tag);
      }
}

The only boring thing is that the Android device needs to be unlocked, because the device turns off the NFC chip when it is asleep. This is quite annoying but due to safety reasons it makes sense. You can go around that though, but you will have to root your Android device.

 

/Johanna

Hooking up the voice intercom to Slack

The intercom from the street to our office was connected to an old GSM phone, and letting people in required us to pick up the phone and press the digit 5. Simple, sure, but it doesn’t feel like 2015. I had also been meaning to try the Twilio API for some time.

Task: Connect the Intercom to Slack.

The Twilio API is great, so it was actually really easy to accomplish. I bought a local number and asked our landlord to forward the Intercom to it. Then I set up this simple TwiML script on a web server:

<Response>
  <Say>Welcome to Earth People. Please stand by.</Say>
  <Enqueue waitUrl="http://x.urtp.pl/slack/service_porttelefon_wait.php"> </Enqueue>
</Response>

What happens here is that Twilio will pick up the call and greet the intercom user using Twilio’s text to speech service. It will then put the “call” into a new phone queue. Twilio will generate a phone queue id and pass it via POST to the waitUrl, below:

<?php
header("content-type: text/xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
file_put_contents('input_porttelefon.txt', $_POST['QueueSid']);
# curl post to slack
$data = array(
  "channel" => "#general",
  "username" => "Intercom",
  "text" => "Meep Meep! Open the door with 'Intercom'",
  "icon_emoji" => ":door:"
);
$url_send = "https://hooks.slack.com/services/xxx/xxx/xxx";
$str_data = json_encode($data);
$ch = curl_init($url_send);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $str_data);
curl_exec($ch);
curl_close($ch);
?>
<Response>
  <Play>http://x.urtp.pl/slack/DrOctagon_EarthPeople.mp3</Play>
</Response>

The waitUrl will save the phone queue id to a text file, make an incoming webhook to Slack and of course play a funky tune to the intercom user. Now Twilio will play the funky tune until it ends, or an incoming HTTP-request is made to the Twilio REST API, using the queue id.

Slack is set up to make an outgoing webhook to a URL. This URL just pops the only call in the queue to the front of the queue.

<?php
# when incoming hook: read call sid from file and call twilio.
$QueueSid = file_get_contents('input_porttelefon.txt');
if(strlen($QueueSid)>2){
  require_once('TwilioPHP/Twilio.php');
  $sid = "xxx";
  $token = "xxx";
  $client = new Services_Twilio($sid, $token);
  $member = $client->account->queues->get($QueueSid)->members->get("Front");
  $member->update(array(
    "Url" => "http://x.urtp.pl/slack/service_porttelefon_dtmf.xml",
    "Method" => "GET"
  ));
  file_put_contents('input_porttelefon.txt', '');
  echo '{"text": "Ok, opened."}';
}else{
  echo '{"text": "No one is at the door."}';
}

The last step is to have Twilio play the sound of the digit 5, which opens the door, using the same kind of XML as step 1. Easy!

Next up is to let intercom users, which we don’t let in for some reason, leave a voice message which is posted to Slack. Totally doable, but not we’re quite there yet.

/Peder

Figurerunning a corgi

I like running, and figurerunning makes it even more fun. Don’t know what figurerunning is? Oh! Basically it’s just GPS tracking helping runners draw dicks on maps while running. What’s not to like?

We made a tool helping runners being creative while planning their routes. In this proof of concept we let users draw figures which automatically snap to roads and bike paths. For every waypoint we store latitude and longitude together with directions. This way we can play audio cues to the user while running.

Below is a GIF showing the proof of concept.

output

Turns out it’s really hard to draw good routes! I tried to draw a corgi for our NYC-based Emelie to run, but there weren’t any corgi shaped neighbourhoods in Brooklyn. It just barely looks like a dog (a corgi doesn’t look like a dog either so well…).

/peder

How to make a wind chime door bell

Our doorbell was already connected to the Internet via an Arduino, but we couldn’t stand the sound it made. We needed a warmer sound, a sound that didn’t make us want to kill the doorbell users (mostly clients).

Step 1:
Buy an old Wind Chime, preferably with a coconut base (because awesome).
2015-01-22 14.34.26

Step 2:
Pick up a few Littlebit modules (Cloudbit, servo motor and power supply).
2015-01-22 15.41.33

Step 3:
Drill, glue and duct tape a bambu stick to the servo motor. Hide all the crap in a classy box.
download_20150122_143926

Step 4:
Make the old Slack door bell script also make a little curl post to the Cloudbit.

<?php
$data = array(
	"percent" => "100",
	"duration_ms" => "10000"
);
$url_send = "https://api-http.littlebitscloud.cc/devices/XXX/output";
$str_data = http_build_query($data);
$ch = curl_init($url_send);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer XXX','Accept: application/vnd.littlebits.v2+json'));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $str_data);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 
echo curl_exec($ch);
curl_close($ch);

Step 5:
Ring the door bell. Voila!
doorbell

Integrera Swish i egna tjänster

I’ll write this in Swedish because it only applies to Swedes.

Swish är ju en väldigt lyckad tjänst. Den har, liksom t.ex. Uber, förändrat ett beteende. Över 2 millioner svenskar använder appen. Självklart borde man göra något smart på Internet med detta, nu när nästan hela Sverige har appen i fickan. Dock har Swish inget publikt API, vilket förmodligen är anledningen till att det inte hänt så mycket ännu.

Det man vill uppnå är att låta en egen tjänst/kampanj/server veta när en viss användare skickat pengar till ett visst Swishanslutet nummer. Och genom att använda lite tveksamma metoder gick det vägen.

Koden är alldeles för risig för att dela med sig av, men ett första proof of concept funkar. Här t.ex. skickar min vän Bruno 1 krona:

foto_2015-01-07_10_41_12

…och här tar jag emot den på vår utvecklingsserver:

apidump

Känns såklart lite olustigt att haxa fulkod mot sina egna banktjänster, men det kändes viktigt att komma i mål. Får se vad det blir för projekt av detta, men kul att det går iaf. Puss.

/peder

 

Why doesn’t all my Instagram embeds in WordPress look the same?

Well, Instagram changed the response on their Oembed service sometime in December 2014.
And WordPress caches all oembed lookups the first time they’re displayed.

This means that all of your little blog posts which had an Instagram image or video (and more than 1 pageview) before December 2014 will look different than all your new posts. There’s no simple way of making all new embeds look old. But the other way around is easy.

1. Open your PhpMyAdmin or similar tool
2. Select the database WordPress is using
3. Click SQL and run this oneliner:

DELETE FROM `wp_postmeta` WHERE `meta_key` LIKE '%oembed_%'

Reload your WordPress site and voilá. That should do it.

An API for Faking Swedish user details

Lorem Ipsum is great for generating place holder texts, and Placehold.it (and our own Place-A-Pet!) does the same job for images. Sometimes though, you need to create fake Swedish users. Say you want to generate dummy comments or fake forum users. What to do?

I just created a little webservice for this. Get a HTML-table by going here:
http://earthpeople.club/~peder/files/persons/?limit=5

…or get a JSON-feed by adding &format=json to the url, like this:
http://earthpeople.club/~peder/files/persons/?limit=5&format=json

Enjoy :)

Let Slack nag you that it’s time to return that commuter pass

Another week, and yet another Slack integration here at Earth People.

This time we’ve added an integration to the public transport system here in Stockholm.

At Earth People we like to use public transportation when going to meetings with clients. That’s why we have bought a couple of commuter passes that we can use whenever we need to go to a meeting.

It works great. But there is one problem however: we only have a few cards, so when you return from the meeting you must remember to return the card so others can use it. And that’s were our latest integration comes into play:

Screenshot showing a message posted to a slack channel, with the date and time of a journey Yup, shortly after the card has been used, a message from the card will appear in our slack channel. That works pretty good as a reminder.

And as a plus feature it also shows how much cash that is left on the card, so we will also know when it’s time to refill the card.

There you have it: another super useful integration for Slack.

Oh! And thanks to mysl for their API-wrapper for “Mitt SL”, that led us into the right direction when researching the API.

Let Slack tell you what songs are currently playing in the office

At Earth People, there’s always music in the air (and the birds sing a pretty song).
Most of the stuff we play is fairly unknown to most of us, and in many cases only played once, ever.

To get rid of all the “Hey what’s this song”-chatter or Slack, I made a litte script which we run as a Slack Command.
The script just checks all the Last.fm-feeds of the people usually playing music, and returns what’s on.

Screenshot 2014-10-15 16.51.02

By no means magic, but adds another bit of homey feeling to Slack.
Here’s the Gist, be our guest.

<?php

date_default_timezone_set("Europe/Stockholm");

require 'vendor/autoload.php';

use GuzzleHttp\Pool;
use GuzzleHttp\Client;

$client = new Client();

$users = array('suprape','musikmarskinen','dafeather','brunobrandstrom','Algoritm','mjelle','hjalle','johannagrip','tumde');

$requests = array();
foreach($users as $user){
	$requests[] = $client->createRequest('GET', 'http://ws.audioscrobbler.com/2.0/', [
		'query' => array(
			'method' => 'user.getrecenttracks',
			'limit' => '5',
			'api_key' => 'GetThisFromLastFm',
			'format' => 'json',
			'nowplaying' => 'true',
			'user' => $user
		)
	]);	
}

$results = Pool::batch($client, $requests);
$nowplaying = array();
foreach ($results->getSuccessful() as $response) {
	$recenttracks = json_decode($response->getBody()->getContents());
	if(isset($recenttracks->recenttracks->track)){
		foreach($recenttracks->recenttracks->track as $track){
			if(isset($track->{"@attr"})){
				$temp = new StdClass();
				$temp->artist = $track->artist->{"#text"};
				$temp->title = $track->name;
				$temp->album = $track->album->{"#text"};
				$temp->image = $track->image[3]->{"#text"};
				$nowplaying[] = $temp;
				unset($temp);
			};
		}
	}
}

header('Content-type: text/plain;charset=utf-8');
echo "Verkar som att ".count($nowplaying)." låtar spelas på kontoret just nu:\n";
foreach($nowplaying as $track){
	echo '- "'.$track->artist.' - '. $track->title.'"';
	if($track->album){
		echo ' från skivan "'.$track->album.'"';
	}
	echo "\n";
}