Eurovision Sweep with Twilio Functions

May 11, 2018
Written by
Stuart Logan
Contributor
Opinions expressed by Twilio contributors are their own

Pht-Vugar_Ibadov_eurovision_(30)

As an Australian, I never got the buzz about Eurovision. For those of you not familiar, it’s a huge annual televised song contest involving the nations of Europe where each country submits an original song and performs live. But now that I live in London it seems appropriate to be getting excited about my first one since I moved!

My wife came up with the idea of having a Eurovision party, involving a sweep where attendees would be randomly assigned a country, bet a nominal amount, and bring a national dish from that country. The app won’t do the cooking, but it will conveniently provide a link to Wikipedia to provide the details and link to the voting page to see if you are in the money.

She was too busy to sit down and work out a sweep, so I figured, working at Twilio, there must be an easy solution. And there is!

First, I had to get all the finalists. I installed requests and beautifulSoup libraries, and then used them to web scrape all the info I needed. I dumped this into a JSON file called euroSweep.json. The code below shows how requests gets a web page and then use beautifulSoup to get the elements of the page that I needed to get the country names.

pip install requests bs4

 

import requests, bs4, json

res = requests.get("https://eurovision.tv/story/exclusive-running-order-eurovision-2018-grand-final")

res.raise_for_status()

euroSoup = bs4.BeautifulSoup(res.text, "html.parser")
elems = euroSoup.select("span['data-text']")
countries = []
for e in elems:
    raw_country = e.getText()
    print(raw_country[0].isdigit())
    if raw_country[0].isdigit():
        country_name = raw_country[4:].lower()
        wiki_name = ""
        euro_name = ""
        if country_name.startswith('the'):
            euro_name = country_name[4:]
            wiki_name = country_name[4:]
        elif " " in country_name:
            country_split = country_name.split()
            euro_name = "-".join(country_split)
            wiki_name = "_".join(country_split)
        else:
            euro_name = country_name
            wiki_name = country_name
        country = {
            "country": country_name,
            "wiki": "https://en.wikipedia.org/wiki/{}".format(wiki_name),
            "euro_world": "https://eurovisionworld.com/eurovision/{}".format(euro_name)
        }
        countries.append(country)

with open('euroSweep.json', 'w') as es:
    json.dump(countries, es)

Next, I needed somewhere to host everything. I was out of time to run up my own server, so I turned to Twilio Functions and Assets. The first step was to upload my JSON file to Assets. Make sure you upload it as a Private Asset or it won’t be available to your Function!

assets

Next, I had to create a Function. Functions are lightweight, event-triggered pieces of code that run in the cloud and scale automatically. Best of all you create them in your own Twilio Console. The Function I created accepted an SMS containing a number of attendees and returned a random, unique country for each person by SMS. The code for this is below. You’ll also need to Name your Function, set the event Dropdown to Incoming Messages and set a path.

/*
   After you have deployed your Function, head to your phone number and configure the inbound SMS handler to this Function
*/

exports.handler = function(context, event, callback) {
    
    let twiml = new Twilio.twiml.MessagingResponse();
    let num_players = 0;
    try {
        num_players = parseInt(event.Body.slice(16));
    }
    catch(err) {
        twiml.message("Invalid Message. Try Eurovisionsweep followed by the number of people \n e.g Eurovisionsweep 3");
    }
    if (typeof num_players == 'number') {
        if (num_players > 26) {
        twiml.message("Sorry, maximum 26 people! Try again");
        } else {
            let country_indexes = [];
        
            let fs = require('fs');
            let file = Runtime.getAssets()['euroSweep.json'].path;
            let obj = JSON.parse(fs.readFileSync(file, 'utf8'));

            let nums = [];
            for (let i = 0; i < 26; i++){
                nums.push(i);
            }
            
            let selections = [];
        
            // randomly pick one from the array
            for (let n = 0; n < num_players; n++) {
                let index = Math.floor(Math.random() * nums.length);
                selections.push(nums[index]);
                nums.splice(index, 1);
            }
            
            let countries = []
            for (let c = 0; c < selections.length; c++) {
                let index = selections[c];
                twiml.message("Contestant " + (c + 1) + ":\n" + obj[index].country + "!\n" + "Eurovision Voting: " + obj[index].euro_world + "\nWikipedia: " + obj[index].wiki)
            }
        }
    } else {
        twiml.message("Invalid Message. Try Eurovisionsweep followed by the number of people \n e.g Eurovisionsweep 3");
    }
    callback(null, twiml);
};

The last step was to connect my Function to a phone number. In fact why not make it available across Europe (and Australia, since we are special guest stars after all!)

Through the Console I purchased numbers for my required countries and configured Incoming Messages to point to my shiny new Function!

numbers

Why not try it out?!

Text eurovisionsweep followed by the number of participants to one of the numbers below, eg eurovisionsweep 5 if there are five people in your sweep

Australia: +61437863876

Estonia: +37259120269

Germany: +4915735981364

Ireland: + 353861800407

Sweden: +46769439050

UK: +447481341033

 

The final result