Skip to content

Instantly share code, notes, and snippets.



Last active Aug 29, 2015
What would you like to do?
Anti MSN Spam plugin (for pidgin)
Author: Natt Piyapramote <nattster at googlemail dot com>
modified from message-filter.c written by Huy Phan <>
# include <config.h>
#include <glib.h>
#include "debug.h"
#include "internal.h"
#include "plugin.h"
#include "pluginpref.h"
#include "prefs.h"
#include "version.h"
#define PLUGIN_ID "core-nattster-anti_msn_spam"
typedef struct {
GTimeVal timestamp; // timestamp of last message received
gint num_msg; // number of normal messages (i.e. message with no URL)
} HVal;
static GHashTable* msg_hash;
static int spam_blocked = 0;
static PurplePluginPrefFrame *
get_plugin_pref_frame(PurplePlugin *plugin) {
PurplePluginPrefFrame *frame;
PurplePluginPref *ppref;
frame = purple_plugin_pref_frame_new();
ppref = purple_plugin_pref_new_with_name_and_label("/plugins/core/anti_msn_spam/spam_blocked",
_("Spams blocked"));
purple_plugin_pref_frame_add(frame, ppref);
return frame;
static gboolean
receiving_im_msg_cb(PurpleAccount *account, char **sender, char **buffer,
PurpleConversation *conv, PurpleMessageFlags *flags, void *data)
HVal* val;
gchar* sender_dup;
GTimeVal now;
gint normal_msg = 1;
// test if incoming message contain URL?
if(g_regex_match_simple(".*(http://|www).*", *buffer, G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
normal_msg = 0;
} else if(g_strrstr(*buffer, "has nudged you!")) // nudge?
return TRUE; // reject it and don't count it as normal msg
// get hash table HVal of sender
val = g_hash_table_lookup(msg_hash, *sender);
if(val == NULL)
sender_dup = g_strdup(*sender);
val = g_new(HVal, 1);
val->num_msg = 0;
g_hash_table_insert(msg_hash, sender_dup, val);
// update num_msg and timestamp
} else {
if(val->num_msg == 0 || now.tv_sec - val->timestamp.tv_sec > 3600)
purple_debug_info(PLUGIN_ID, "caught SPAM from %s - %s\n", *sender, *buffer);
return TRUE; // reject the message
return FALSE; // accept it
static gboolean
plugin_load(PurplePlugin *plugin)
void *conv_handle = purple_conversations_get_handle();
spam_blocked = purple_prefs_get_int("/plugins/core/anti_msn_spam/spam_blocked");
msg_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
purple_signal_connect(conv_handle, "receiving-im-msg", plugin, PURPLE_CALLBACK(receiving_im_msg_cb), NULL);
return TRUE;
static gboolean
plugin_unload(PurplePlugin *plugin)
purple_debug_info(PLUGIN_ID, "unloading...\n");
purple_prefs_set_int("/plugins/core/anti_msn_spam/spam_blocked", spam_blocked);
return TRUE;
static PurplePluginUiInfo prefs_info = {
0, /* page_num (Reserved) */
NULL, /* frame (Reserved) */
/* Padding */
static PurplePluginInfo info = {
PURPLE_PLUGIN_MAGIC, /* Plugin magic, this must always be
PURPLE_MAJOR_VERSION, /* This is also defined in libpurple. It helps
libpurple's plugin system determine which version
of libpurple this plugin was compiled for, and
whether loading it will cause problems. */
PURPLE_MINOR_VERSION, /* See previous */
PURPLE_PLUGIN_STANDARD, /* PurplePluginType: There are 4 different values for
this field. The first is PURPLE_PLUGIN_UNKNOWN,
which should not be used. The second is
PURPLE_PLUGIN_STANDARD; this is the value most
plugins will use. Next, we have PURPLE_PLUGIN_LOADER;
this is the type you want to use if your plugin will
make it possible to load non-native plugins. For
example, the Perl and Tcl loader plugins are of this
type. Last, we have PURPLE_PLUGIN_PROTOCOL. If your
plugin is going to allow the user to connect to
another network, this is the type you'd want to use. */
NULL, /* This field is the UI requirement. If you're writing
a core plugin, this must be NULL and the plugin must
not contain any UI code. If you're writing a Pidgin
plugin, you need to use PIDGIN_PLUGIN_TYPE. If you
are writing a Finch plugin, you would use
0, /* This field is for plugin flags. Currently, the only
flag available to plugins is invisible
(PURPLE_PLUGIN_FLAG_INVISIBLE). It causes the plugin
NOT to appear in the list of plugins. */
NULL, /* This is a GList of plugin dependencies. In other words,
it's a GList of plugin id's that your plugin depends on.
Set this value to NULL no matter what. If your plugin
has dependencies, set them at run-time in the
plugin_init function. */
PURPLE_PRIORITY_DEFAULT,/* This is the priority libpurple will give your plugin.
There are three possible values for this field,
PLUGIN_ID, /* This is your plugin's id. There is a whole page dedicated
to this in the Related Pages section of the API docs. */
"Anti MSN Spam", /* This is your plugin's name. This is what will be
displayed for your plugin in the UI. */
"1.0", /* This is the version of your plugin. */
"MSN spam filter", /* This is the summary of your plugin. It should be a short
blurb. The UI determines where, if at all, to display
this. */
"MSN spam filter, check for unwanted SPAM messages and block them", /* This is the description of your plugin. It can be as long
and as descriptive as you like. And like the summary,
it's up to the UI where, if at all, to display this (and
how much to display). */
"Natt Piyapramote <>", /* This is where you can put your name and e-mail
address. */
"",/* This is the website for the plugin. This tells users
where to find new versions, report bugs, etc. */
plugin_load, /* This is a pointer to a function for libpurple to call when
it is loading the plugin. It should be of the type:
gboolean plugin_load(PurplePlugin *plugin)
Returning FALSE will stop the loading of the plugin.
Anything else would evaluate as TRUE and the plugin will
continue to load. */
plugin_unload, /* Same as above except it is called when libpurple tries to
unload your plugin. It should be of the type:
gboolean plugin_unload(PurplePlugin *plugin)
Returning TRUE will tell libpurple to continue unloading
while FALSE will stop the unloading of your plugin. */
NULL, /* Similar to the two above members, except this is called
when libpurple tries to destory the plugin. This is
generally only called when for some reason or another the
plugin fails to probe correctly. It should be of the type:
void plugin_destroy(PurplePlugin *plugin) */
NULL, /* This is a pointer to a UI-specific struct. For a Pidgin
plugin it will be a pointer to a PidginPluginUiInfo
struct, for example. */
NULL, /* This is a pointer to either a PurplePluginLoaderInfo
struct or a PurplePluginProtocolInfo struct, and is
beyond the scope of this document. */
&prefs_info, /* This is a pointer to a PurplePluginUiInfo struct. It is
a core/ui split way for core plugins to have a UI
configuration frame. You can find an example of this
code in libpurple/plugins/pluginpref_example.c */
NULL, /* This is a function pointer where you can define "plugin
actions". The UI controls how they're displayed. It
should be of the type:
GList *function_name(PurplePlugin *plugin,
gpointer context)
It must return a GList of PurplePluginActions. */
static void
init_plugin(PurplePlugin *plugin)
purple_prefs_add_int("/plugins/core/anti_msn_spam/spam_blocked", 0);
PURPLE_INIT_PLUGIN(anti_msn_spam, init_plugin, info)
#!/usr/bin/env python
Anti MSN Spam for Pidgin
version 1.0
by Natt Piyapramote <nattster at gmail dot com>
How it works?
it will classify all messages received into 2 categories.
1. IM that contains URL
2. Normal IM
This script will count number of Normal IM for each user in your contact list.
If someone that didn't send you any "Normal IM", but send you an "IM that contains URL" as a first message. Then, this IM is most likely to be a SPAM. This software will reject.
# Anti MSN spam for pidgin
import time
import re
url_re = re.compile('.*(http://|www.).*')
purple = None
last_normal = {} # timestamp of last Normal IMs
num_normal = {} # number of Normal IMs
def receiving_msg(account, sender, message, conversation, flags):
"Receiving message handler"
global purple, last_normal, num_normal, url_re
# Initialize or reset last_normal, num_normal values
if not last_normal.has_key(sender):
last_normal[sender] = 0
if not num_normal.has_key(sender) or time.time() - last_normal[sender] > 3600:
num_normal[sender] = 0
# test if message contains URL
m = message.replace("\n", "").replace("\r", "")
if url_re.match(m):
# it's an URL message
if num_normal[sender] == 0:
# if first message contains URL, it's most likely to be a SPAM
print 'message ', message, ' from ', sender, ' canceled'
# it's a normal message
num_normal[sender] += 1
last_normal[sender] = time.time()
import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
bus = dbus.SessionBus()
obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
loop = gobject.MainLoop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.