twitter newsfeed daemon

This commit is contained in:
psyc://psyced.org/~lynX 2009-04-18 08:09:05 +02:00
parent a32a31794e
commit 217f34fd42
11 changed files with 154 additions and 16 deletions

View File

@ -35,6 +35,23 @@ ________________________________________________________________________
im psyc://psyced.org/~lynx Context festgestellt. im psyc://psyced.org/~lynx Context festgestellt.
? who's gonna clean up the mess of having too many websites ? ? who's gonna clean up the mess of having too many websites ?
- when provided with a _focus pointing to yourself, _request_execute will
still try to forward a command to the "current place" - a uniform or
object is passed to parsecmd(data, dest) but dest is ignored later in cmd()
- _source_relay should point to tg, not foo (only happens with local users)
.
:_source.psyc://x-net.hu/~foo
:_target.psyc://3e44aa49.XXX:-52801/
:_source_relay.psyc://x-net.hu/~foo
:_nick.tg
:_time_INTERNAL.1239787956
:_nick_target.psyc://x-net.hu/~foo
_message_private
halo
.
________________________________________________________________________ ________________________________________________________________________
== psyced 1.0 ========================================================== == psyced 1.0 ==========================================================
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

View File

@ -696,6 +696,9 @@ _message_public
_message_friends _message_friends
|[_nick] {_TEXT_action_shouts}: [_data] |[_nick] {_TEXT_action_shouts}: [_data]
_message_twitter
|[_nick] ... [_data]
_message_echo_private _message_echo_private
|You tell [_nick_target]: [_data] |You tell [_nick_target]: [_data]

View File

@ -95,6 +95,7 @@
// nosave? static? volatile. only for variables, not methods! // nosave? static? volatile. only for variables, not methods!
// another nice word for the opposite of persistent would be "shed" // another nice word for the opposite of persistent would be "shed"
#define volatile nosave #define volatile nosave
#define persistent
// every lpc dialect has its own foreach syntax. aint that cute? // every lpc dialect has its own foreach syntax. aint that cute?
#define each(ITERATOR, LIST) foreach(ITERATOR : LIST) #define each(ITERATOR, LIST) foreach(ITERATOR : LIST)

View File

@ -372,6 +372,8 @@ object compile_object(string file) {
// match both http:/ and https:/ objects ;D // match both http:/ and https:/ objects ;D
if (abbrev("http", file)) { if (abbrev("http", file)) {
rob = clone_object(HTTP_PATH "fetch"); rob = clone_object(HTTP_PATH "fetch");
// driver has the habit of removing double slash in object name
file = replace(file, ":/", "://");
if (rob) rob->fetch(file[..<3]); if (rob) rob->fetch(file[..<3]);
return rob; return rob;
} }

View File

@ -83,6 +83,7 @@
// nosave? static? volatile. no idea if pike has something like this // nosave? static? volatile. no idea if pike has something like this
#define volatile static #define volatile static
#define persistent
// every lpc dialect has its own foreach syntax. aint that cute? // every lpc dialect has its own foreach syntax. aint that cute?
#define each(ITEM, LIST) foreach(LIST; ; ITEM) #define each(ITEM, LIST) foreach(LIST; ; ITEM)

View File

@ -2,8 +2,8 @@
// //
// generic HTTP GET client, mostly used for RSS - // generic HTTP GET client, mostly used for RSS -
// but we could fetch any page or data with it, really // but we could fetch any page or data with it, really
// tobij even made the object have the URL as its object name. fancy! ;) // tobij even allowed the object to have the URL as its object name. fancy! ;)
//
#include <ht/http.h> #include <ht/http.h>
#include <net.h> #include <net.h>
#include <uniform.h> #include <uniform.h>
@ -24,7 +24,7 @@ volatile string modificationtime, etag, http_message;
volatile string useragent = SERVER_VERSION; volatile string useragent = SERVER_VERSION;
volatile int http_status, port, fetching, ssl; volatile int http_status, port, fetching, ssl;
volatile string buffer, thehost, url, fetched, host, resource; volatile string buffer, thehost, url, fetched, host, resource;
volatile string basicauth; volatile string basicauth = "";
int parse_status(string all); int parse_status(string all);
int parse_header(string all); int parse_header(string all);
@ -33,22 +33,23 @@ int buffer_content(string all);
string qHost() { return thehost; } string qHost() { return thehost; }
void fetch(string murl) { void fetch(string murl) {
if (url) return; if (url) return;
url = replace(murl, ":/", "://"); // accept.c does this for us:
P3(("%O: fetch(%O)\n", ME, url)) //url = replace(murl, ":/", "://");
connect(); // so we can use this method also in a normal way
url = murl;
P3(("%O: fetch(%O)\n", ME, url))
connect();
} }
object load() { return ME; } object load() { return ME; }
void setHTTPBasicAuth(string user, string password) { void sAuth(string user, string password) {
basicauth = "Authorization: Basic " + encode_base64(user + ":" + password) + "\r\n"; basicauth = "Authorization: Basic "+
encode_base64(user +":"+ password) +"\r\n";
} }
string sAgent(string a) { string sAgent(string a) { return useragent = a; }
PT(("sAgent(%O) in %O\n", a, ME))
return useragent = a;
}
// net/place/news code follows. // net/place/news code follows.
@ -65,7 +66,7 @@ void connect() {
thehost = lower_case(thehost); thehost = lower_case(thehost);
ssl = t == "s"; ssl = t == "s";
} }
P3(("URL, THEHOST: %O, %O\n", url, thehost)) PT(("URL, THEHOST: %O, %O\n", url, thehost))
unless (port) unless (port)
unless (sscanf(thehost, "%s:%d", thehost, port) == 2) unless (sscanf(thehost, "%s:%d", thehost, port) == 2)
port = ssl? HTTPS_SERVICE: HTTP_SERVICE; port = ssl? HTTPS_SERVICE: HTTP_SERVICE;
@ -84,7 +85,7 @@ varargs int real_logon(int arg) {
unless (url) return -3; unless (url) return -3;
unless (resource) sscanf(url, "%s://%s/%s", scheme, host, resource); unless (resource) sscanf(url, "%s://%s/%s", scheme, host, resource);
buffer = ""; buffer = basicauth;
if (modificationtime) if (modificationtime)
buffer += "If-Modified-Since: "+ modificationtime + "\r\n"; buffer += "If-Modified-Since: "+ modificationtime + "\r\n";
if (useragent) buffer += "User-Agent: "+ useragent +"\r\n"; if (useragent) buffer += "User-Agent: "+ useragent +"\r\n";
@ -93,7 +94,7 @@ varargs int real_logon(int arg) {
// we won't need connection: close w/ http/1.0 // we won't need connection: close w/ http/1.0
//emit("Connection: close\r\n\r\n"); //emit("Connection: close\r\n\r\n");
PT(("%O using %O\n", ME, buffer)) PT(("%O using %O\n", ME, buffer))
emit("GET /"+ resource +" HTTP/1.0\r\n" emit("GET /"+ resource +" HTTP/1.1\r\n"
"Host: "+ host +"\r\n" "Host: "+ host +"\r\n"
+ buffer + + buffer +
"\r\n"); "\r\n");

View File

@ -367,6 +367,7 @@ mapping jsonObject()
} }
if (nextClean() != ':') { if (nextClean() != ':') {
PT(("jsonFAIL: '%c' at %O\n", nextClean(), myIndex))
THROW("Expected a ':' after a key.\n"); THROW("Expected a ':' after a key.\n");
} }

View File

@ -1315,6 +1315,7 @@ case "_message_echo_private":
// fall thru // fall thru
case "_message_echo_public": case "_message_echo_public":
case "_message_echo": case "_message_echo":
case "_message_twitter":
case "_message_public": case "_message_public":
// avoid treating this as _message here // avoid treating this as _message here
break; break;
@ -1323,6 +1324,9 @@ case "_message_audio":
// not being displayed to users other than psyc clients // not being displayed to users other than psyc clients
data = 0; data = 0;
break; break;
// we should judge our messages by their routing method, not by their
// name! thus, the _public and _private distinction has to exist only
// for display. FIXME
case "_message": case "_message":
// this is only visible in person.c, not user.c // this is only visible in person.c, not user.c
// therefore probably useless // therefore probably useless

View File

@ -1354,6 +1354,7 @@ msg(source, mc, data, mapping vars) {
else unless (!source else unless (!source
|| qAllowExternal(&source, mc, vars) // mayExternal hook || qAllowExternal(&source, mc, vars) // mayExternal hook
|| MEMBER(source) || MEMBER(source)
|| source == "/" // allow root to send everywhere
|| isValidRelay(source) || isValidRelay(source)
|| isValidRelay(vars["_source_relay"]) || isValidRelay(vars["_source_relay"])
|| isValidRelay(vars["_context"])) { || isValidRelay(vars["_context"])) {

106
world/net/twitter/polly.c Normal file
View File

@ -0,0 +1,106 @@
// vim:foldmethod=marker:syntax=lpc:noexpandtab
//
// yeah yeah twitter.. why twitter?
// http://about.psyc.eu/Twitter
#include <net.h>
persistent int lastid;
volatile object feed;
parse(string body) {
mixed wurst;
string nick;
object o;
mapping d, p;
int i;
//body = read_file("/net/twitter/many.json");
if (!body || body == "") {
P1(("%O failed to get its timeline.\n", ME))
return;
}
//#if DEBUG > 0
rm(DATA_PATH "timeline.json");
write_file(DATA_PATH "timeline.json", body);
P4((body))
//#endif
unless (pointerp(wurst = parse_json(body))) {
P1(("%O failed to parse its timeline.\n", ME))
return;
}
i=sizeof(wurst)-1;
if (wurst[i]["id"] <= lastid) {
P1(("%O received old stuff.\n", ME))
return;
}
lastid = wurst[i]["id"];
save_object(DATA_PATH "twitter");
for (; i>=0; i--) {
d = wurst[i];
unless (mappingp(d)) {
P1(("%O got a broken tweet: %O.\n", ME, d))
continue;
}
p = d["user"];
unless (mappingp(p)) {
P1(("%O got a userless tweet.\n", ME))
continue;
}
unless (nick = p["screen_name"]) {
P1(("%O got a nickless tweeter.\n", ME))
continue;
}
PT((" %O", nick))
o = find_place(nick);
// _notice_update_twitter ?
sendmsg(o, "_message_twitter", d["text"], ([
// should i send text as _action?
"_nick": nick,
// _count seems to be the better word for this
"_amount_followers": p["followers_count"],
"_color": "#"+ p["profile_text_color"],
"_description": p["description"],
"_uniform_photo": p["profile_image_url"],
"_page": p["url"],
"_name": p["name"],
// "_contact_twitter": p["id"],
"_description_agent_HTML": d["source"],
"_reference_reply": d["in_reply_to_screen_name"],
// "_twit": d["id"],
]), "/"); // send as root
// der spiegel u.a. twittern übrigens in latin-1
// während psyc utf-8 erwartet.. eine char guess engine
// muss her.. FIXME
}
}
fetch() {
feed -> content( #'parse, 0, 1 );
feed -> fetch("http://twitter.com/statuses/friends_timeline.json"
"?since_id="+ lastid +"&count=123");
call_out( #'fetch, 9 * 60 );
}
create() {
mapping config;
object o = find_object(CONFIG_PATH "config");
if (o) config = o->qConfig();
if (!config) {
P1(("No configuration for twitter gateway found.\n");
destruct(ME);
return;
}
restore_object(DATA_PATH "twitter");
// we could even choose to inherit this instead...
feed = clone_object(HTTP_PATH "fetch");
//feed -> sAgent(SERVER_VERSION " builtin Twitter to PSYC gateway");
feed -> sAuth(config["nickname"], config["password"]);
call_out( #'fetch, 14 );
}

View File

@ -753,6 +753,7 @@ case "_message_announcement":
case "_message_behaviour_warning": case "_message_behaviour_warning":
case "_message_behaviour_punishment": case "_message_behaviour_punishment":
case "_message_behaviour": case "_message_behaviour":
case "_message_twitter":
unless (stringp(data)) variant = "_default"; unless (stringp(data)) variant = "_default";
else variant = ""; else variant = "";
#ifdef USE_THE_NICK #ifdef USE_THE_NICK