You are not logged in.
I created a couple of very simple plugins for libpurple (Pidgin) and weechat for use with dzen.
These are extremely basic and do nothing more than what I require, therefore you will likely want to modify them for your own usage. There is loads of room for improvement
To create fifos
mkfifo ~/.purple/fifo
mkfifo ~/.weechat/fifo
pidginsrc/libpurple/plugins/messagefifo.c
#define PURPLE_PLUGINS
#include <stdio.h>
#include "conversation.h"
#include "signals.h"
#include "version.h"
char temp[256];
static void
write_fifo(const char *buffer)
{
FILE *pipe;
if ((pipe = fopen("/home/HOMEDIR/.purple/fifo", "w"))) { // ATTENTION, REPLACE HOMEDIR WITH YOUR USERNAME
fprintf(pipe, "^tw()%s\n%s\n", buffer, buffer);
fclose(pipe);
}
}
static void
buddy_signed_on_cb(PurpleBuddy *buddy, void *data)
{
snprintf(temp, sizeof(temp), "^fg(lightblue)%s ^fg()has signed ^fg(white)on", purple_buddy_get_alias(buddy));
write_fifo(temp);
}
static void
buddy_signed_off_cb(PurpleBuddy *buddy, void *data)
{
snprintf(temp, sizeof(temp), "^fg(lightblue)%s ^fg()has signed ^fg(white)off", purple_buddy_get_alias(buddy));
write_fifo(temp);
}
static void
received_im_msg_cb(PurpleAccount *account, char *sender, char *buffer, PurpleConversation *conv, PurpleMessageFlags flags, void *data)
{
snprintf(temp, sizeof(temp), "^fg(lightblue)%s ^fg()says, ^fg(grey50)%s", purple_buddy_get_alias(purple_find_buddy(account, sender)), buffer);
write_fifo(temp);
}
static void
buddy_typing_cb(PurpleAccount *account, const char *name, void *data)
{
snprintf(temp, sizeof(temp), "^fg(lightblue)%s ^fg()is typing...", purple_buddy_get_alias(purple_find_buddy(account, name)));
write_fifo(temp);
}
/*static void
buddy_typing_stopped_cb(PurpleAccount *account, const char *name, void *data)
{
sprintf(temp, "%s has stopped typing", purple_buddy_get_alias(purple_find_buddy(account, name)));
write_fifo(temp);
}*/
static gboolean
plugin_load(PurplePlugin *plugin)
{
void *blist_handle = purple_blist_get_handle();
void *conv_handle = purple_conversations_get_handle();
/* Buddy List subsystem signals */
purple_signal_connect(blist_handle, "buddy-signed-on",
plugin, PURPLE_CALLBACK(buddy_signed_on_cb), NULL);
purple_signal_connect(blist_handle, "buddy-signed-off",
plugin, PURPLE_CALLBACK(buddy_signed_off_cb), NULL);
/* Conversations subsystem signals */
purple_signal_connect(conv_handle, "received-im-msg",
plugin, PURPLE_CALLBACK(received_im_msg_cb), NULL);
purple_signal_connect(conv_handle, "buddy-typing",
plugin, PURPLE_CALLBACK(buddy_typing_cb), NULL);
/* purple_signal_connect(conv_handle, "buddy-typing-stopped",
plugin, PURPLE_CALLBACK(buddy_typing_stopped_cb), NULL);*/
return TRUE;
}
static PurplePluginInfo info =
{
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD, /**< type */
NULL, /**< ui_requirement */
0, /**< flags */
NULL, /**< dependencies */
PURPLE_PRIORITY_DEFAULT, /**< priority */
"messagefifo", /**< id */
"Message Fifo", /**< name */
"1.0", /**< version */
/** summary */
"Writes some messages to a ~/.purple/fifo",
/** description */
"Writes some messages to a ~/.purple/fifo",
"Zane Ashby <zane.a@demonastery.org>", /**< author */
"http://demonastery.org", /**< homepage */
plugin_load, /**< load */
NULL, /**< unload */
NULL, /**< destroy */
NULL, /**< ui_info */
NULL, /**< extra_info */
NULL,
NULL,
/* Padding */
NULL,
NULL,
NULL,
NULL
};
static void
init_plugin(PurplePlugin *plugin)
{
}
PURPLE_INIT_PLUGIN(messagefifo, init_plugin, info)
Compile with (IIRC)
make messagefifo.so
mv messagefifo.so ~/.purple/plugins/
weechat/plugins/messagefifo.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "weechat-plugin.h"
char plugin_name[] = "MessageFIFO";
char plugin_version[] = "0.1";
char plugin_description[] = "Output incoming messages into fifo";
t_plugin_handler *privmsg;
int mf_privmsg (t_weechat_plugin *plugin, int argc, char **argv, char *handler_args, void *handler_pointer)
{
FILE *pipe;
if ((pipe = fopen("/home/HOMEDIR/.weechat/fifo", "w"))) { // ATTENTION, CHANGE HOMEDIR
char temp[256];
char *tok, *dp;
dp = strdup(argv[2]);
tok = strtok(dp, ":!");
snprintf(temp, sizeof(temp), "^fg(lightblue)%s ^fg()says,^fg(grey50)", tok);
tok = strtok(NULL, ":!");
tok = strtok(NULL, ":!");
snprintf(temp, sizeof(temp), "%s %s", temp, tok);
while ((tok = strtok(NULL, "")) != NULL) {
snprintf(temp, sizeof(temp), "%s:%s", temp, tok);
}
fprintf(pipe, "^tw()%s\n%s\n", temp, temp);
fclose(pipe);
free(dp);
} else {
plugin->print(plugin, NULL, NULL, "Failed to open fifo");
}
return PLUGIN_RC_OK;
}
int weechat_plugin_init (t_weechat_plugin *plugin)
{
privmsg = plugin->msg_handler_add (plugin, "PRIVMSG", &mf_privmsg, NULL, NULL);
if (privmsg != NULL) {
return PLUGIN_RC_OK;
} else {
plugin->print(plugin, NULL, NULL, "Failed to add Message Handler");
return PLUGIN_RC_KO;
}
}
void weechat_plugin_end (t_weechat_plugin *plugin)
{
plugin->handler_remove(plugin, privmsg);
}
Compile with
gcc -fPIC -Wall -c messagefifo.c
gcc -shared -fPIC -o messagefifo.so messagefifo.o
To use with dzen (adjust accordingly)
tail -f ~/.weechat/fifo | dzen2 -l 10 -fn "-*-terminus-*-*-*-*-12-*-*-*-*-*-*-*" -ta c -y 754 -w 512 -x 0 &
tail -f ~/.purple/fifo | sed -u -e 's@<[/]*[A-Za-z][A-Za-z0-9]*[^>]*>@@g' -e 's@[&]nbsp;@ @g' -e 's@[&]amp;@\&@g' -e 's@[&]quot;@"@g' -e 's@[&][a-z][a-z]*;@ @g' "$@" | dzen2 -l 10 -fn "-*-terminus-*-*-*-*-12-*-*-*-*-*-*-*" -ta c -y 754 -w 512 -x 512 &
Last edited by HashBox (2009-04-23 02:46:27)
Offline
Another one to wake the display when a message is received, using DPMS. Doubt this will be useful to others but it serves as an example.
#define PURPLE_PLUGINS
#include <X11/Xlib.h>
#include <X11/extensions/dpms.h>
#include "conversation.h"
#include "signals.h"
#include "version.h"
static void
received_im_msg_cb(PurpleAccount *account, char *sender, char *buffer, PurpleConversation *conv, PurpleMessageFlags flags, void *data)
{
Display *dpy = XOpenDisplay(NULL);
if (dpy) {
DPMSEnable(dpy); // Enable DPMS
DPMSForceLevel(dpy, DPMSModeOn); // Force Monitor ON
XCloseDisplay(dpy);
}
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
void *conv_handle = purple_conversations_get_handle();
[url][/url]
purple_signal_connect(conv_handle, "received-im-msg", plugin, PURPLE_CALLBACK(received_im_msg_cb), NULL);
return TRUE;
}
static PurplePluginInfo info =
{
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD,
NULL,
0,
NULL,
PURPLE_PRIORITY_DEFAULT,
"wake-on-message",
"Wake On Message",
"1.0",
"Wakes display on message receive",
"Wakes display on message receive",
"Zane Ashby <zane.a@demonastery.org>",
"http://demonastery.org",
plugin_load,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
static void
init_plugin(PurplePlugin *plugin)
{
}
PURPLE_INIT_PLUGIN(wake-on-message, init_plugin, info)
Offline
The mkfifo plugins look pretty cool. I might have to fool with them some this weekend.
: () { : | :& } ;:
Offline
Yes! Damn I almost forgot, I subscribed earlier to this thread without reacting, thanks a lot for the idea! I made something that might interest you..
I'm running finch in a screen session on a shell server somewhere, therefore I don't have any notifications if someone starts talking to me when the screen is detached... This kind of plug-in was right what needed!
I modified the script to put a file in my public_html dir with nicknames of buddies who have unanswered conversations. To get the notifications, a script on my home box curl'ed the file every 10 seconds and piped that into dzen, nothing extraordinary..
As you can see the file gets erased as soon as I type something back. Although there might be better ways to do it, this was a reasonable trigger to me, since most of my contacts say reply-worthy things most of the time.
But! This isn't exactly what happens anymore.... since I still need dzen to get my notifications, it means my home box has to be turned on, which defeats the use of a shell server, only a little......
Therefore, Ladies and Gentlemen, I present you a new way of getting notified: Google Calendar SMS notifications.
As soon as I get talked to in a conversation that doesn't have my attention*, a Google Calendar-event is created by the Python-script below (This was a quite of a pain in the wuts, luckily I didn't have to use any Google-specific modules... The script only uses mechanize which doesn't come standard with Arch Python), and as friendly as Google is: they instantly send me a free SMS saying I have a new meeting (called (L)~°(*)LY4eVaH sNoOpy(*)°~(L) or similar). My sms notifications are configured to come 0 minutes before the event and if you don't specify a specific hour when creating an event, it is scheduled 'now'.
*This has to change to something more elegant in the plugin, one message per conversation would be less spammy for starting... Of course other things are SMS'able too... but please don't be evil with the second script!
plugin:
#define PURPLE_PLUGINS
#include <stdio.h>
#include "conversation.h"
#include "signals.h"
#include "version.h"
char temp[256];
static void
write_file(const char *buffer)
{
FILE *pipe;
if ((pipe = fopen("/home/gondil/public_html/fifo", "a"))) { // ATTENTION, REPLACE HOMEDIR WITH YOUR USERNAME
fprintf(pipe, "%s\n", buffer);
fclose(pipe);
}
system("echo $(sort -u /home/gondil/public_html/fifo) > /home/gondil/public_html/fifo");
system("/home/gondil/calendar &");
}
static void
empty_file(const char *buffer)
{
FILE *pipe;
if ((pipe = fopen("/home/gondil/public_html/fifo", "w"))) { // ATTENTION, REPLACE HOMEDIR WITH YOUR USERNAME
fprintf(pipe, "", buffer);
fclose(pipe);
}
}
static void
received_im_msg_cb(PurpleAccount *account, char *sender, char *buffer, PurpleConversation *conv, PurpleMessageFlags flags, void *data)
{
if (purple_conversation_has_focus(conv)!= 1) {
snprintf(temp, sizeof(temp), "%s", purple_buddy_get_alias(purple_find_buddy(account, sender)));
write_file(temp);
}
}
static void
sent_im_msg_cb(PurpleAccount *account, const char *receiver, const char *message, void *data)
{
snprintf(temp, sizeof(temp), "", message);
empty_file(temp);
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
void *conv_handle = purple_conversations_get_handle();
purple_signal_connect(conv_handle, "received-im-msg",
plugin, PURPLE_CALLBACK(received_im_msg_cb), NULL);
purple_signal_connect(conv_handle, "sent-im-msg",
plugin, PURPLE_CALLBACK(sent_im_msg_cb), NULL);
return TRUE;
}
static PurplePluginInfo info =
{
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD, /**< type */
NULL, /**< ui_requirement */
0, /**< flags */
NULL, /**< dependencies */
PURPLE_PRIORITY_DEFAULT, /**< priority */
"messagefile", /**< id */
"Message File", /**< name */
"1.1", /**< version */
/** summary */
"Writes some nicknames to a file",
/** description */
"Writes some nicknames to a file",
"Zane Ashby and gondil", /**< author */
NULL, /**< homepage */
plugin_load, /**< load */
NULL, /**< unload */
NULL, /**< destroy */
NULL, /**< ui_info */
NULL, /**< extra_info */
NULL,
NULL,
/* Padding */
NULL,
NULL,
NULL,
NULL
};
static void
init_plugin(PurplePlugin *plugin)
{
}
PURPLE_INIT_PLUGIN(messagefifo, init_plugin, info)
calendar event creator:
#!/usr/bin/python
import mechanize
import sys
import urllib2
# Create a mechanize browser
mech = mechanize.Browser()
mech.set_handle_robots(False)
# With Goggles desired UAgent
mech.addheaders = [("User-agent", "Java/1.5.0_06")]
# See the G-API: We need a some token here, that is displayed by /~pkelchte/ (mah personal php site on ESAT that only does that)
mech.open("https://www.google.com/accounts/AuthSubRequest?scope=http://www.google.com/calendar/feeds/&session=1&secure=0&next=http://homes.esat.kuleuven.be/~pkelchte/index.php")
# But before we get the token, we need to submit our user-data
mech.select_form(nr=0)
mech["Email"] = "" # REPLACE THIS WITH YOUR GOOGLE ACCOUNT
mech["Passwd"] = "" # AND THIS WITH YOUR PASSWORD
try:
mech.submit()
except:
print "Did not submit credentials"
sys.exit("Error in submitting credentials")
# Because that's not enough, we need to confirm that we want to go trough with this and submit another form...
mech.select_form(nr=0)
mech.submit(id='allow')
# By now, we have one token, but it's not the final token! We need *another* token, called the AuthSubSessionToken, sigh...
mech.addheaders = [("Authorization", "AuthSub token=\"" + mech.response().read() + "\""), ("User-agent", "Java/1.5.0_06")]
mech.open("http://www.google.com/accounts/AuthSubSessionToken")
# A bunch of tokens later...
# Let's use urllib2 to do this POST request (some xml-y thing is the string you would manually type in the "New event" box on Google Calendar)
# Encore some headers
headers = {"Authorization": "AuthSub token=\"" + mech.response().read().replace("\n","").split("=")[1] + "\"", "User-Agent": "Java/1.5.0_06", "Content-Type": "application/atom+xml"}
# Read the file that we're interested in! Damn, it's so interesting!!
file = open('/home/gondil/public_html/fifo', 'r')
message = file.read()
file.close()
# The actual event
event = """
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gCal='http://schemas.google.com/gCal/2005'>
<content type="html">""" + message + """</content>
<gCal:quickadd value="true"/>
</entry>
"""
req = urllib2.Request("http://www.google.com/calendar/feeds/default/private/full", event, headers)
calresponse = urllib2.urlopen(req)
# Normally, we stop here... but since Google likes traffic, we need to go to a slightly different url, with the same headers and POST data
req2 = urllib2.Request(calresponse.geturl(), event, headers)
try:
calresponse2 = urllib2.urlopen(req2)
except:
# print "You can check but normally this is a 201 CREATED response or something, I don't really care... It's my code, right :P"
pass
#this might not even work, it's only here to remind me
#print urllib2.urlopen(urllib2.Request("http://www.google.com/calendar/feeds/default/owncalendars/full","",headers)).read()
index.php: (If you don't want to use that esat.kuleuven site for this... because probably I'm not going to keep this on index.php forever...)
<?php
print $_POST['token'];
print $_GET['token'];
?>
Offline
Really nice stuff gondil!
I had played around with a script to send myself sms using the gcalcli tool, but decided I should probably refrain from using that for obvious reasons hehe.
I can't imagine how painful that must've been to get that mechanize script working right, but it looks great! (definitely serves as an intro for me in using mechanize )
I might see if I can modify that for my own personal use, not sure why I didn't think to hook something like this up to libpurple earlier
I do have one question though, do you know how feasible it would be to remove those calendar entries after they have been "used"? That was one of the things keeping me from using this sms hack a bit more.
Offline
Oh sweet! I was wondering what I would have to do to get notification in dzen from weechat! Was gonna hack up one of the plugins.
Offline
Oh sweet! I was wondering what I would have to do to get notification in dzen from weechat! Was gonna hack up one of the plugins.
I must point out that unless dzen is capturing the output of the fifo then weechat will actually be frozen and blocking as far as I'm aware, I've had it happen accidentally on occasion. I must look into fixing that :\ I can only assume the same happens with the libpurple version as well.
Offline
Hi HashBox! I was so lazy first and just occasionally cleared out my whole calendar when it got too messy...
It seemed not that much extra work to delete the newly created event because Google sends back a unique ID, below is the code! (I inelegantly inserted a sleep of 20s before doing the deletion, of course you can change it (I don't know if it is needed, I think I'm at my SMS limit for today, couldn't test it, lol))
At least I can see that it does what you would expect (saw it by clicking refresh on the calendar website while executing the script)
Mechanize is handy indeed! It originated in Perl but some people are pushing me to write in Python... (To be honest, I don't feel very comfortable yet but hey... as long as it works hehe)
I find it strange for example that there seemed no other way to retrieve what Google sends back when posting the event than catching the HTTPError and read()'ing it... (Receiving a 201 would not be an expected reason to throw an exception, I thought at first... but the whole urllib2 response gets lost when it happens)
Apart from this all: have fun with it and please post if you have new ideas!!
#!/usr/bin/python
import mechanize
import sys
import urllib2
import httplib
from urllib2 import HTTPError
# Create a mechanize browser
mech = mechanize.Browser()
mech.set_handle_robots(False)
# With Goggles desired UAgent
mech.addheaders = [("User-agent", "Java/1.5.0_06")]
# See the G-API: We need a some token here, that is displayed by /~pkelchte/ (mah personal php site on ESAT that only does that)
mech.open("https://www.google.com/accounts/AuthSubRequest?scope=http://www.google.com/calendar/feeds/&session=1&secure=0&next=http://homes.esat.kuleuven.be/~pkelchte/index.php")
# But before we get the token, we need to submit our user-data
mech.select_form(nr=0)
mech["Email"] = ""
mech["Passwd"] = ""
try:
mech.submit()
except:
print "Did not submit credentials"
sys.exit("Error in submitting credentials")
# Because that's not enough, we need to confirm that we want to go trough with this and submit another form...
mech.select_form(nr=0)
mech.submit(id='allow')
# By now, we have one token, but it's not the final token! We need *another* token, called the AuthSubSessionToken, sigh...
mech.addheaders = [("Authorization", "AuthSub token=\"" + mech.response().read() + "\""), ("User-agent", "Java/1.5.0_06")]
mech.open("http://www.google.com/accounts/AuthSubSessionToken")
# A bunch of tokens later...
# Let's use urllib2 to do this POST request (some xml-y thing is the string you would manually type in the "New event" box on Google Calendar)
# Encore some headers
authsub = "AuthSub token=\"" + mech.response().read().replace("\n","").split("=")[1] + "\""
headers = {"Authorization": authsub, "User-Agent": "Java/1.5.0_06", "Content-Type": "application/atom+xml", "GData-Version": "2"}
file = open('/home/gondil/public_html/fifo', 'r')
message = file.read()
file.close()
# The actual event
event = """
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gCal='http://schemas.google.com/gCal/2005'>
<content type="html">""" + message + """</content>
<gCal:quickadd value="true"/>
</entry>
"""
req = urllib2.Request("http://www.google.com/calendar/feeds/default/private/full", event, headers)
calresponse = urllib2.urlopen(req)
# Normally, we stop here... but since Google likes traffic, we need to go to a slightly different url, with the same headers and POST data
req2 = urllib2.Request(calresponse.geturl(), event, headers)
try:
calresponse2 = urllib2.urlopen(req2)
# You can check but normally this is a 201 CREATED response or something, I don't really care... It's my code, right :P
except HTTPError, e :
# I placed this sleep to give the event at least a 20 second lifetime (poor, poor event...)
import time
time.sleep(20)
# Retrieve the event's edit url
eventurl = e.read().split("<link rel='edit' type='application/atom+xml' href='http://www.google.com")[1].split("'/>")[0]
# The Deletion has to be done via httplib, because Google wants a DELETE request (urllib2 only handles GET and POST)
conn = httplib.HTTPConnection("www.google.com")
conn.request("DELETE", eventurl, "", headers)
calresponse3 = conn.getresponse()
# Again, they like to have a little more traffic, we need to append a session ID to that last url (we can find it in the redirect page)
eventurl2 = calresponse3.read().split("HREF=\"")[1].split("\"")[0]
# Ooh and here there is need of a new header, no questions please
headers2 = {"Authorization": authsub, "User-Agent": "Java/1.5.0_06", "Content-Type": "application/atom+xml", "GData-Version": "2", "If-Match": "*"}
conn.request("DELETE", eventurl2, "", headers2)
calresponse4 = conn.getresponse()
# No errors? Ok we can close the connection
conn.close()
Last edited by gondil (2009-04-23 22:35:05)
Offline
Oh wow that's awesome! I will definitely make use of this now Thanks!
Edit: I've decided to go the non hacky route and bought 800 SMS credits from Clickatell, and would definitely recommend them at this stage. Your choice of API, HTTP(S) being the simplest. I can now do "sendsms.py <message> [optional number]", just need to hook this up to a notification system so I can be alerted to stuff when my pidgin status is Away
Last edited by HashBox (2009-04-28 12:34:58)
Offline