view autoreply/autoreply.c @ 1028:314cfd774bc4

s/purple.guifications.org/plugins.guifications.org/
author Paul Aurich <paul@darkrain42.org>
date Thu, 06 Aug 2009 12:30:12 -0700
parents 1096a41b674d
children
line wrap: on
line source

/*
 * Autoreply - Autoreply feature for all the protocols
 * Copyright (C) 2005-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02111-1301, USA.
 */

/* If you can't figure out what this line is for, DON'T TOUCH IT. */
#include "../common/pp_internal.h"

#define PLUGIN_ID			"core-plugin_pack-autoreply"
#define PLUGIN_STATIC_NAME	"Autoreply"
#define PLUGIN_AUTHOR		"Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>"

/* Purple headers */
#include <account.h>
#include <accountopt.h>
#include <blist.h>
#include <conversation.h>
#include <plugin.h>
#include <pluginpref.h>
#include <request.h>
#include <savedstatuses.h>
#include <status.h>
#include <util.h>

#define	PREFS_PREFIX		"/plugins/core/" PLUGIN_ID
#define	PREFS_IDLE			PREFS_PREFIX "/idle"
#define	PREFS_AWAY			PREFS_PREFIX "/away"
#define	PREFS_GLOBAL		PREFS_PREFIX "/global"
#define	PREFS_MINTIME		PREFS_PREFIX "/mintime"
#define	PREFS_MAXSEND		PREFS_PREFIX "/maxsend"
#define	PREFS_USESTATUS		PREFS_PREFIX "/usestatus"
#define	PREFS_PREFIX_MSG    PREFS_PREFIX "/prefix"
#define PREFS_X_INVISIBLE     PREFS_PREFIX "/invisible"

typedef struct _PurpleAutoReply PurpleAutoReply;
typedef struct _AutoReplyProtocolOptions	AutoReplyProtocolOptions;

struct _PurpleAutoReply
{
	PurpleBuddy *buddy;
	char *reply;
};

struct _AutoReplyProtocolOptions {
	PurpleAccountOption *message;
	PurpleAccountOption *off;
};

typedef enum
{
	STATUS_NEVER,
	STATUS_ALWAYS,
	STATUS_FALLBACK
} UseStatusMessage;

static GHashTable *options = NULL;

/**
 * Returns the auto-reply message for buddy
 */
static const char *
get_autoreply_message(PurpleBuddy *buddy, PurpleAccount *account)
{
	const char *reply = NULL;
	UseStatusMessage use_status;

	use_status = purple_prefs_get_int(PREFS_USESTATUS);
	if (use_status == STATUS_ALWAYS)
	{
		PurpleStatus *status = purple_account_get_active_status(account);
		PurpleStatusType *type = purple_status_get_type(status);
		if (purple_status_type_get_attr(type, "message") != NULL)
			reply = purple_status_get_attr_string(status, "message");
		else
			reply = purple_savedstatus_get_message(purple_savedstatus_get_current());
	}

	if ((!reply || !*reply) && buddy)
	{
		/* Is there any special auto-reply for this buddy? */
		reply = purple_blist_node_get_string((PurpleBlistNode*)buddy, "autoreply");

		if ((!reply || !*reply) && PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy))
		{
			/* Anything for the contact, then? */
			reply = purple_blist_node_get_string(((PurpleBlistNode*)buddy)->parent, "autoreply");
		}
	}

	if (!reply || !*reply)
	{
		/* Is there any specific auto-reply for this account? */
		reply = purple_account_get_string(account, "autoreply", NULL);
	}

	if (!reply || !*reply)
	{
		/* Get the global auto-reply message */
		reply = purple_prefs_get_string(PREFS_GLOBAL);
	}

	if (*reply == ' ' || *reply == '\0')
		reply = NULL;

	if (!reply && use_status == STATUS_FALLBACK)
		reply = purple_status_get_attr_string(purple_account_get_active_status(account), "message");

	return reply;
}

static void
written_msg(PurpleAccount *account, const char *who, const char *message,
				PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
{
	PurpleBuddy *buddy;
	PurplePresence *presence;
	const char *reply = NULL;
	gboolean trigger = FALSE;

	if (!(flags & PURPLE_MESSAGE_RECV))
		return;

	if (!message || !*message)
		return;

	/* Do not send an autoreply for an autoreply or a 'delayed' (offline?) message */
	if (flags & (PURPLE_MESSAGE_AUTO_RESP | PURPLE_MESSAGE_DELAYED))
		return;

	if(purple_account_get_bool(account, "ar_off", FALSE))
		return;

	g_return_if_fail(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM);

	presence = purple_account_get_presence(account);

	if (purple_prefs_get_bool(PREFS_X_INVISIBLE) &&
			purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE))
		return;

	if (purple_prefs_get_bool(PREFS_AWAY) && !purple_presence_is_available(presence))
		trigger = TRUE;
	if (purple_prefs_get_bool(PREFS_IDLE) && purple_presence_is_idle(presence))
	   trigger = TRUE;

	if (!trigger)
		return;

	buddy = purple_find_buddy(account, who);
	reply = get_autoreply_message(buddy, account);

	if (reply)
	{
		PurpleConnection *gc;
		PurpleMessageFlags flag = PURPLE_MESSAGE_SEND;
		time_t last_sent, now;
		int count_sent, maxsend;
		char *send = NULL;
		const char *prefix;

		last_sent = GPOINTER_TO_INT(purple_conversation_get_data(conv, "autoreply_lastsent"));
		now = time(NULL);

		/* Have we spent enough time after our last autoreply? */
		if (now - last_sent >= (purple_prefs_get_int(PREFS_MINTIME)*60))
		{
			count_sent = GPOINTER_TO_INT(purple_conversation_get_data(conv, "autoreply_count"));
			maxsend = purple_prefs_get_int(PREFS_MAXSEND);

			/* Have we sent the autoreply enough times? */
			if (count_sent < maxsend || maxsend == -1)
			{
				purple_conversation_set_data(conv, "autoreply_count", GINT_TO_POINTER(++count_sent));
				purple_conversation_set_data(conv, "autoreply_lastsent", GINT_TO_POINTER(now));
				gc = purple_account_get_connection(account);
				prefix = purple_prefs_get_string(PREFS_PREFIX_MSG);
				if (gc->flags & PURPLE_CONNECTION_AUTO_RESP) {
					flag |= PURPLE_MESSAGE_AUTO_RESP;
					prefix = NULL;  /* The prpl knows about auto-response. So ignore the prefix string. */
				}
				send = g_strdup_printf("%s%s", prefix ? prefix : "", reply);
				purple_conv_im_send_with_flags(PURPLE_CONV_IM(conv), send, flag);
				g_free(send);
			}
		}
	}
}

static void
set_auto_reply_cb(PurpleBlistNode *node, char *message)
{
	if (!message || !*message)
		message = " ";
	purple_blist_node_set_string(node, "autoreply", message);
}

static void
set_auto_reply(PurpleBlistNode *node, gpointer plugin)
{
	char *message;
	PurpleBuddy *buddy;
	PurpleAccount *account;
	PurpleConnection *gc;

	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
		buddy = (PurpleBuddy *)node;
	else
		buddy = purple_contact_get_priority_buddy((PurpleContact*)node);

	account = purple_buddy_get_account(buddy);
	gc = purple_account_get_connection(account);

	/* XXX: There should be a way to reset to the default/account-default autoreply */

	message = g_strdup_printf(_("Set autoreply message for %s"),
					purple_buddy_get_contact_alias(buddy));
	purple_request_input(plugin, _("Set Autoreply Message"), message,
					_("The following message will be sent to the buddy when "
						"the buddy sends you a message and autoreply is enabled."),
					get_autoreply_message(buddy, account), TRUE, FALSE,
					(gc->flags & PURPLE_CONNECTION_HTML) ? "html" : NULL,
					_("_Save"), G_CALLBACK(set_auto_reply_cb),
					_("_Cancel"), NULL,
					account, purple_buddy_get_name(buddy), NULL, node);
	g_free(message);
}

static void
context_menu(PurpleBlistNode *node, GList **menu, gpointer plugin)
{
	PurpleMenuAction *action;

	if (purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)
		return;

	if (!PURPLE_BLIST_NODE_IS_BUDDY(node) && !PURPLE_BLIST_NODE_IS_CONTACT(node))
		return;

	action = purple_menu_action_new(_("Set _Autoreply Message"),
					PURPLE_CALLBACK(set_auto_reply), plugin, NULL);
	(*menu) = g_list_prepend(*menu, action);
}

static void
add_options_for_protocol(PurplePlugin *plg)
{
	AutoReplyProtocolOptions *arpo;
	PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(plg);

	arpo = g_new(AutoReplyProtocolOptions, 1);

	arpo->message = purple_account_option_string_new(_("Autoreply message"),
													 "autoreply", NULL);
	arpo->off = purple_account_option_bool_new(_("Turn off autoreply"),
													 "ar_off", FALSE);
	info->protocol_options = g_list_append(info->protocol_options,
										   arpo->message);
	info->protocol_options = g_list_append(info->protocol_options, arpo->off);

	if (!g_hash_table_lookup(options, plg))
		g_hash_table_insert(options, plg, arpo);
}

static void
remove_options_for_protocol(PurplePlugin *plg)
{
	PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(plg);
	AutoReplyProtocolOptions *arpo = g_hash_table_lookup(options, plg);
	GList *l = NULL;

	if(!arpo)
		return;

	/*
	 * 22:55 < sadrul> grim: the check when removing is required, iirc, when
	 *                 pidgin quits, and a prpl is unloaded before the plugin
	 */
	if ((l = g_list_find(info->protocol_options, arpo->message)))
	{
		info->protocol_options = g_list_remove_link(info->protocol_options, l);
		purple_account_option_destroy(arpo->message);
	}
	if ((l = g_list_find(info->protocol_options, arpo->off)))
	{
		info->protocol_options = g_list_remove_link(info->protocol_options, l);
		purple_account_option_destroy(arpo->off);
	}

	g_hash_table_remove(options, plg);
	g_free(arpo);
}

static void
plugin_load_cb(PurplePlugin *plugin, gboolean load)
{
	if (plugin->info && plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
	{
		if (load)
			add_options_for_protocol(plugin);
		else
			remove_options_for_protocol(plugin);
	}
}

static gboolean
plugin_load(PurplePlugin *plugin)
{
	GList *list;

	purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", plugin,
						PURPLE_CALLBACK(written_msg), NULL);
	purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin,
						PURPLE_CALLBACK(context_menu), plugin);
	purple_signal_connect(purple_plugins_get_handle(), "plugin-load", plugin,
						PURPLE_CALLBACK(plugin_load_cb), GINT_TO_POINTER(TRUE));
	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", plugin,
						PURPLE_CALLBACK(plugin_load_cb), GINT_TO_POINTER(FALSE));

	/* Perhaps it's necessary to do this after making sure the prpl-s have been loaded? */
	options = g_hash_table_new(g_direct_hash, g_direct_equal);
	list = purple_plugins_get_protocols();
	while (list)
	{
		add_options_for_protocol(list->data);
		list = list->next;
	}

	return TRUE;
}

static gboolean
plugin_unload(PurplePlugin *plugin)
{
	GList *list;

	if (options == NULL)
		return TRUE;

	list = purple_plugins_get_protocols();
	while (list)
	{
		remove_options_for_protocol(list->data);
		list = list->next;
	}
	g_hash_table_destroy(options);
	options = NULL;

	return TRUE;
}

static PurplePluginPrefFrame *
get_plugin_pref_frame(PurplePlugin *plugin)
{
	PurplePluginPrefFrame *frame;
	PurplePluginPref *pref;

	frame = purple_plugin_pref_frame_new();

	pref = purple_plugin_pref_new_with_label(_("Send autoreply messages when"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_AWAY,
					_("When my account is _away"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_IDLE,
					_("When my account is _idle"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_GLOBAL,
					_("_Default reply"));
	purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_STRING_FORMAT);
	purple_plugin_pref_set_format_type(pref,
				PURPLE_STRING_FORMAT_TYPE_MULTILINE | PURPLE_STRING_FORMAT_TYPE_HTML);
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_PREFIX_MSG,
					_("Autoreply Prefix\n(only when necessary)"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_X_INVISIBLE,
					_("Do not autoreply when invisible."));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_label(_("Status message"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_USESTATUS,
						_("Autoreply with status message"));
	purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
	purple_plugin_pref_add_choice(pref, _("Never"),
						GINT_TO_POINTER(STATUS_NEVER));
	purple_plugin_pref_add_choice(pref, _("Always when there is a status message"),
						GINT_TO_POINTER(STATUS_ALWAYS));
	purple_plugin_pref_add_choice(pref, _("Only when there's no autoreply message"),
						GINT_TO_POINTER(STATUS_FALLBACK));

	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_label(_("Delay between autoreplies"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_MINTIME,
					_("_Minimum delay (mins)"));
	purple_plugin_pref_set_bounds(pref, 0, 9999);
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_label(_("Times to send autoreplies"));
	purple_plugin_pref_frame_add(frame, pref);

	pref = purple_plugin_pref_new_with_name_and_label(PREFS_MAXSEND,
					_("Ma_ximum count"));
	purple_plugin_pref_set_bounds(pref, 0, 9999);
	purple_plugin_pref_frame_add(frame, pref);

	return frame;
}

static PurplePluginUiInfo prefs_info = {
	get_plugin_pref_frame,
	0,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static PurplePluginInfo info = {
	PURPLE_PLUGIN_MAGIC,		/* Magic				*/
	PURPLE_MAJOR_VERSION,		/* Purple Major Version	*/
	PURPLE_MINOR_VERSION,		/* Purple Minor Version	*/
	PURPLE_PLUGIN_STANDARD,		/* plugin type			*/
	NULL,						/* ui requirement		*/
	0,							/* flags				*/
	NULL,						/* dependencies			*/
	PURPLE_PRIORITY_DEFAULT,	/* priority				*/

	PLUGIN_ID,					/* plugin id			*/
	NULL,						/* name					*/
	PP_VERSION,					/* version				*/
	NULL,						/* summary				*/
	NULL,						/* description			*/
	PLUGIN_AUTHOR,				/* author				*/
	PP_WEBSITE,					/* website				*/

	plugin_load,				/* load					*/
	plugin_unload,				/* unload				*/
	NULL,						/* destroy				*/

	NULL,						/* ui_info				*/
	NULL,						/* extra_info			*/
	&prefs_info,				/* prefs_info			*/
	NULL,						/* actions				*/

	NULL,						/* reserved 1			*/
	NULL,						/* reserved 2			*/
	NULL,						/* reserved 3			*/
	NULL						/* reserved 4			*/
};

static void
init_plugin(PurplePlugin *plugin)
{
#ifdef ENABLE_NLS
	bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */

	info.name = _("Autoreply");
	info.summary = _("Autoreply for all the protocols");
	info.description = _("This plugin lets you set autoreply message for any "
			"protocol. You can set the global autoreply message from the "
			"plugin options dialog. To set some specific autoreply message for "
			"a particular buddy, right click on the buddy in the buddy-list "
			"window. To set autoreply messages for some accounts, go to the "
			"`Advanced' tab of the account edit dialog.");

	purple_prefs_add_none(PREFS_PREFIX);
	purple_prefs_add_bool(PREFS_IDLE, TRUE);
	purple_prefs_add_bool(PREFS_AWAY, TRUE);
	purple_prefs_add_string(PREFS_GLOBAL, _("I am currently not available. Please leave your message, "
							"and I will get back to you as soon as possible."));
	purple_prefs_add_int(PREFS_MINTIME, 10);
	purple_prefs_add_int(PREFS_MAXSEND, 10);
	purple_prefs_add_int(PREFS_USESTATUS, STATUS_NEVER);
	purple_prefs_add_string(PREFS_PREFIX_MSG, _("This is an autoreply: "));
	purple_prefs_add_bool(PREFS_X_INVISIBLE, TRUE);
}

PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)