dinsdag 28 juli 2009

Cron jobs van appspot.com

Are you not Dutch? But English is no problem? Click here to go to the English version.



Deze post is eigenlijk een voortzetting van de vorige, het plaatsen van berichten op je website met Twitter. De berichten worden ieder kwartier bijgewerkt. Tenminste, bij de eerste bezoeker die op onze website van de drumband komt. Als niemand op de website zou komen en alleen het twitter-account zou bekijken, zouden de berichten nooit geupdate worden!

Toen kwam Google in beeld. Je kunt namelijk bij www.appspot.com een account aanmaken, en daarbij heb je ook CRON beschikbaar! Ik had de nodige moeite om het aan de praat te krijgen, maar het is gelukt. Ik geef hieronder het kant en klare overzicht waarmee het zou moeten werken;

STAP 1
Ga naar www.appspot.com, maak een account aan en download de benodigde spullen (Python, Google AppEngine). Dit laatste zorgt dat je als je Windows draait een map c:\program files\Google\Google_appengine krijgt.

STAP 2
Stel dat je testsite.appspot.com aangemaakt hebt. Maak dan in je c:\program files\google een map testsite aan en plaats hier de volgende bestanden met de volgende inhoud in:


cron.yaml
cron:
- description: every quarter of a hour update Twitter entries
url: /tasks/sync_twitter_site
schedule: every 15 minutes

index.yaml:
indexes:

# keep the autogenerated stuff. quite an empy document :)


main.py
#!/usr/bin/env python

import wsgiref.handlers

from google.appengine.ext import webapp
from google.appengine.ext import mail
from google.appengine.ext import urlfetch

class MainHandler(webapp.RequestHandler):

def get(self):
self.response.out.write("This is the main page.")

def main():
application = webapp.WSGIApplication([('/',MainHandler)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)

if 1 == 1:
main()



app.yaml
application:testsite
version: 1
runtime: python
api_version: 1

handlers:
- url: /tasks/sync_twitter_site
script: sync_twittersite.py
login: admin
- url: /.*
script: main.py


sync_twittersite.py
#!/usr/bin/env python

import wsgiref.handlers

from google.appengine.ext import webapp
from google.appengine.ext import mail
from google.appengine.ext import urlfetch

class MainHandler(webapp.RequestHandler):

def get(self):
url = "http://mysite.com/page_to_call.asp"
result = urlfetch(url)
self.response.out.write(result.status_code)

def main():
application = webapp.WSGIApplication([('/.*',MainHandler)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)

if 1 == 1:
main()



Zoals je ziet, de code voor het Python sync-script is bijna hetzelfde als main.py script. Maar het /.* is de clou, als je die .* vergeet werkt het script niet (omdat je binnenkomt met /tasks/...). En dan kreeg je weer een 'JOB FAILED' zondat dat je doorhad waar het nu door kwam.

STAP 3
Zet het spul online. Klik op je startknop, Uitvoeren, CMD en klik op OK.
C:, cd\progra~1, cd google, appcfg.py update ./testsite/

Via www.appspot.com en hier inloggen kun je je CRON acties in de gaten houden. Succes!


ENGLISH VERSION


This post is almost a continuation of the previous post, placing messages on your website with Twitter. The messages are updated each quarter of a hour. At least, when people visit our website, the first one in this quarter triggers the update. So if no one would visit our site and only look at our Twitter account, you would get no updates!

After a little search I found that 'Google is your friend!'. At www.appspot.com you can create a free account, appspot supports CRON jobs! I had quite some difficulties to get this stuff working, but finally I succeeded. I will give you my setup of files and code to help you work it out.

STEP 1
Go to www.appspot.com, create an account and download your stuff (Python, Google AppEngine). The Google AppEngine creates this folder on your PC if you are running Windows: c:\program files\Google\Google_appengine.

STEP 2
In this example we presume you created testsite.appspot.com. Create in your c:\program files\google a folder with the name 'testsite' and put these files (with this content) in it:


cron.yaml
cron:
- description: every quarter of a hour update Twitter entries
url: /tasks/sync_twitter_site
schedule: every 15 minutes

index.yaml:
indexes:

# keep the autogenerated stuff. quite an empy document :)


main.py
#!/usr/bin/env python

import wsgiref.handlers

from google.appengine.ext import webapp
from google.appengine.ext import mail
from google.appengine.ext import urlfetch

class MainHandler(webapp.RequestHandler):

def get(self):
self.response.out.write("This is the main page.")

def main():
application = webapp.WSGIApplication([('/',MainHandler)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)

if 1 == 1:
main()



app.yaml
application:testsite
version: 1
runtime: python
api_version: 1

handlers:
- url: /tasks/sync_twitter_site
script: sync_twittersite.py
login: admin
- url: /.*
script: main.py


sync_twittersite.py
#!/usr/bin/env python

import wsgiref.handlers

from google.appengine.ext import webapp
from google.appengine.ext import mail
from google.appengine.ext import urlfetch

class MainHandler(webapp.RequestHandler):

def get(self):
url = "http://mysite.com/page_to_call.asp"
result = urlfetch(url)
self.response.out.write(result.status_code)

def main():
application = webapp.WSGIApplication([('/.*',MainHandler)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)

if 1 == 1:
main()



As you see, the code for the Python sync-script is almost the same as the main.py script. But the /.* is easily forgotten, which causes not to run (because you enter with /tasks/...).

STEP 3
Let's get it on the Internet. Click on the start-button, Run, CMD and click OK.
C:, cd\progra~1, cd google, appcfg.py update ./testsite/


By www.appspot.com, login here, you can check your CRON tasks. Good luck!

zondag 5 juli 2009

Plaats berichten via Twitter op je website

Are you not Dutch? But English is no problem? Click here to go to the English version.



Ik ben bezig met Twitter te experimenteren. Een eigen Twitter-account heb ik al, aan dat account zit mijn mobiele telefoon gekoppeld. Dus als ik een bericht stuur, komt het op mijn eigen Twitter binnen.

Nu heb ik voor de drumband ook een Twitter account aangemaakt, wat ik eigenlijk wil gaan gebruiken om berichtjes op onze website toe te voegen. Lijkt me wel leuk om op deze manier op 12 juli het 'thuisfront' op de hoogte te houden van onze verrichtingen. Er moet dus een bericht via de mobiel naar de Twitter van de drumband. Je kunt een direct bericht sturen door:
d naamTwitterDrumband bericht te sturen.

Dus eerst de API van Twitter erbij.
http://apiwiki.twitter.com/Things-Every-Developer-Should-Know

Hier staan een paar simpele voorbeelden van curl aanroepen.
Nu heb ik op mijn laptop Ubuntu draaien, met Apache. Ik had al eerder een of andere plugin werkend gekregen, nu moet ik de curl aan de praat krijgen. Als ik in een php pagina het volgende zet:

<?
phpinfo();
?>

krijg je een overzicht van alle functies, instellingen en mogelijkheden van Apache. Hier staat GEEN curl tussen.

We pakken google erbij en typen deze zoekwoorden in:
ubuntu apache get curl working

Een gewone sudo apt-get install curl werkt niet, het volgende werkt beter:
apt-get install php5-curl
(gevonden op http://www.vpsmedia.com/forum/showthread.php?t=5).

Hierna een herstart van Apache:
sudo /etc/init.d/apache2 restart

In mijn phpinfo() staat nu wel netjes een onderdeel curl.
phpinfo vink ik uit, en zet de originele code weer aan.
En ik krijg nu netjes wat XML informatie terug!

<?
//phpinfo();
//exit;
$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://twitter.com/statuses/friends_timeline.xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

// grab URL and pass it to the browser
curl_exec($ch);

// close cURL resource, and free up system resources
curl_close($ch);
?>

Even zoeken, uiteindelijk kom ik op de goeie URL:
http://apiwiki.twitter.com/Twitter-API-Documentation

Onderstaande geeft me de 'direct messages' terug:
<?
//phpinfo();
//exit;
$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages.xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 0);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

// grab URL and pass it to the browser
curl_exec($ch);

// close cURL resource, and free up system resources
curl_close($ch);
?>

Nu nog dat het niet rechtstreeks naar de browser gaat, maar naar een lokale variable
(maak ik een XML document van, dan wordt het parsen straks wat makkelijker);
Dat gaat ook makkelijk, volgende toevoegen:
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // put data in local var

en wijziging:
$result = curl_exec($ch);


Het resultaat bevat de laatste 20 directe berichten. Nu wil ik als ik een bepaald direct
bericht verwerkt heb alleen de berichten zien die later gestuurd zijn. Ook dat kan:
http://twitter.com/direct_messages.xml?since_id=12345

Onderstaande code is het eind van mijn testpagina, ik heb nu voldoende kennis om er netjes een Joomla-module van te maken. Het onderste, uitgevinkte, deel is voor het verwijderen van het directe bericht:

<?
//phpinfo();
//exit;
$ch = curl_init();

// set URL and other appropriate options
//curl_setopt($ch, CURLOPT_URL, "http://twitter.com/statuses/friends_timeline.xml");
//curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages/new.xml");
curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages.xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // put data in local var
curl_setopt($ch, CURLOPT_POST, 0);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

// grab URL and pass it to the browser
$result = curl_exec($ch);
//echo $result;

$doc = new DOMDocument();
$doc->loadXML($result);

$message_id = "";
$sender_id = "";
$message_text = "";
$message_datetime = "";
$sender_screenname = "";

if ($doc->hasChildNodes()){
$messages = $doc->getElementsByTagName('direct_message');
foreach ($messages as $message){
if ($message->hasChildNodes()){

foreach ($message->childNodes as $childNode){
if ($childNode->nodeName != "#text"){
switch ($childNode->nodeName){
case "id":
$message_id = $childNode->nodeValue;
break;
case "sender_id":
$sender_id = $childNode->nodeValue;
break;
case "text":
$message_text = $childNode->nodeValue;
break;
case "created_at":
$message_datetime = $childNode->nodeValue;
break;
case "sender_screen_name":
$sender_screenname = $childNode->nodeValue;
break;
default:
break;
}
}
}

}
}
}

if ($message_id != ""){
echo $message_id . "<HR>";
echo $sender_id . "<HR>";
echo $message_text . "<HR>";
echo $message_datetime . "<HR>";
echo $sender_screenname . "<HR>";
// do stuff (save local in database). then remove the message

}
curl_close($ch);

if ($message_id != ""){
/* $ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages/destroy/" . $message_id .".xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // put data in local var
curl_setopt($ch, CURLOPT_POST, 1);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

curl_exec($ch);

// close cURL resource, and free up system resources
curl_close($ch);
*/
}
?>

ENGLISH VERSION




I am doing some experiments with Twitter. I already have my own account, my mobile phone is connected to it. So when I send a message, it is added on my own Twitter account.

I made an account for the drumband, I want to use it to add messages on our website. We go to the World Music Contest in Kerkrade on july 12th, it is a neat way to inform the people at home how everything is going. So it must be possible to send a message by phone to the Twitter of the drumband. You can send a direct message with the following syntax:
d nameTwitterDrumband MESSAGE

So, first look at the API of Twitter:
http://apiwiki.twitter.com/Things-Every-Developer-Should-Know

Here I find a couple of examples with curl. I work with Ubuntu on my laptop, running PHP and Apache. When I put the following text in a PHP page:

<?
phpinfo();
?>

I get all the settings etc. of Apache (and pc) but there is NO curl in the list.

Google is your friend. So I goto google.nl and enter these words:
ubuntu apache get curl working

A normal sudo apt-get install curl does not work, but this does:
apt-get install php5-curl
(found on http://www.vpsmedia.com/forum/showthread.php?t=5).

Let's restart Apache:
sudo /etc/init.d/apache2 restart

In phpinfo() I do get a curl part now!
I put phpinfo in the comments and enable the original code.
I run it: yes! I get XML!

<?
//phpinfo();
//exit;
$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://twitter.com/statuses/friends_timeline.xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERUSERNAME'.':'.'TWITTERPASSWORD');

// grab URL and pass it to the browser
curl_exec($ch);

// close cURL resource, and free up system resources
curl_close($ch);
?>

I need to search, but finally I get the right docs URL:
http://apiwiki.twitter.com/Twitter-API-Documentation

The below code returns my 'direct messages':
<?
//phpinfo();
//exit;
$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages.xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 0);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

// grab URL and pass it to the browser
curl_exec($ch);

// close cURL resource, and free up system resources
curl_close($ch);
?>

I want to filter the result, so I put the result in a local variable.
Therefore we need to update the code:
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // put data in local var

en change:
$result = curl_exec($ch);

The result contains the last 20 direct messages. I want to retrieve only the messages I haven't processed already. That is possible:
http://twitter.com/direct_messages.xml?since_id=12345

The code below is the end of my testingpage. I have enough knowledge now to make a neat Joomla-module of it. The final, commented, part is used to delete the direct message you processed.

<?
//phpinfo();
//exit;
$ch = curl_init();

// set URL and other appropriate options
//curl_setopt($ch, CURLOPT_URL, "http://twitter.com/statuses/friends_timeline.xml");
//curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages/new.xml");
curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages.xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // put data in local var
curl_setopt($ch, CURLOPT_POST, 0);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

// grab URL and pass it to the browser
$result = curl_exec($ch);
//echo $result;

$doc = new DOMDocument();
$doc->loadXML($result);

$message_id = "";
$sender_id = "";
$message_text = "";
$message_datetime = "";
$sender_screenname = "";

if ($doc->hasChildNodes()){
$messages = $doc->getElementsByTagName('direct_message');
foreach ($messages as $message){
if ($message->hasChildNodes()){

foreach ($message->childNodes as $childNode){
if ($childNode->nodeName != "#text"){
switch ($childNode->nodeName){
case "id":
$message_id = $childNode->nodeValue;
break;
case "sender_id":
$sender_id = $childNode->nodeValue;
break;
case "text":
$message_text = $childNode->nodeValue;
break;
case "created_at":
$message_datetime = $childNode->nodeValue;
break;
case "sender_screen_name":
$sender_screenname = $childNode->nodeValue;
break;
default:
break;
}
}
}

}
}
}

if ($message_id != ""){
echo $message_id . "<HR>";
echo $sender_id . "<HR>";
echo $message_text . "<HR>";
echo $message_datetime . "<HR>";
echo $sender_screenname . "<HR>";
// do stuff (save local in database). then remove the message

}
curl_close($ch);

if ($message_id != ""){
/* $ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "http://twitter.com/direct_messages/destroy/" . $message_id .".xml");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // put data in local var
curl_setopt($ch, CURLOPT_POST, 1);
@curl_setopt($ch , CURLOPT_USERPWD,'TWITTERGEBRUIKERSNAAM'.':'.'TWITTERWACHTWOORD');

curl_exec($ch);

// close cURL resource, and free up system resources
curl_close($ch);
*/
}
?>