Create ZNC plugin

This commit is contained in:
TingPing 2014-09-05 08:35:53 -04:00
parent 2aa3eb8cfb
commit 08ce54bbb5
4 changed files with 421 additions and 1 deletions

View File

@ -123,6 +123,10 @@ AC_ARG_ENABLE(doat,
[AS_HELP_STRING([--disable-doat],[disable the Do At plugin])],
doat=$enableval, doat=yes)
AC_ARG_ENABLE(znc,
[AS_HELP_STRING([--disable-znc],[disable the ZNC plugin])],
znc=$enableval, znc=yes)
AC_ARG_ENABLE(fishlim,
[AS_HELP_STRING([--disable-fishlim],[disable the FiSHLiM plugin])],
fishlim=$enableval, fishlim=yes)
@ -478,6 +482,21 @@ if test "$doat" != "no"; then
fi
fi
dnl *********************************************************************
dnl ** ZNC **************************************************************
dnl *********************************************************************
if test "$znc" != "no"; then
AC_MSG_CHECKING(for plugin interface used by ZNC)
znc=no
if test "$plugin" = yes; then
znc=yes
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([plugins are disabled, use the --enable-plugin option for ZNC])
fi
fi
dnl *********************************************************************
dnl ** FiSHLiM **********************************************************
dnl *********************************************************************
@ -599,6 +618,7 @@ AM_CONDITIONAL(DO_PYTHON, test "x$python" != "xno")
AM_CONDITIONAL(DO_PLUGIN, test "x$plugin" = "xyes")
AM_CONDITIONAL(DO_CHECKSUM, test "x$checksum" = "xyes")
AM_CONDITIONAL(DO_DOAT, test "x$doat" = "xyes")
AM_CONDITIONAL(DO_ZNC, test "x$znc" = "xyes")
AM_CONDITIONAL(DO_FISHLIM, test "x$fishlim" = "xyes")
AM_CONDITIONAL(DO_SYSINFO, test "x$sysinfo" = "xyes")
AM_CONDITIONAL(USE_DBUS, test "x$dbus" = "xyes")
@ -836,6 +856,7 @@ plugins/python/Makefile
plugins/perl/Makefile
plugins/checksum/Makefile
plugins/doat/Makefile
plugins/znc/Makefile
plugins/fishlim/Makefile
plugins/sysinfo/Makefile
po/Makefile.in
@ -864,6 +885,7 @@ echo Python ................ : $python
echo
echo Checksum .............. : $checksum
echo Do At ................. : $doat
echo ZNC Extras ............ : $znc
echo FiSHLiM ............... : $fishlim
echo SysInfo ............... : $sysinfo
echo

View File

@ -10,6 +10,10 @@ if DO_DOAT
doatdir = doat
endif
if DO_ZNC
zncdir = znc
endif
if DO_FISHLIM
fishlimdir = fishlim
endif
@ -22,4 +26,4 @@ if DO_SYSINFO
sysinfodir = sysinfo
endif
SUBDIRS = $(pythondir) $(perldir) $(checksumdir) $(doatdir) $(fishlimdir) $(sysinfodir)
SUBDIRS = $(pythondir) $(perldir) $(checksumdir) $(doatdir) $(fishlimdir) $(sysinfodir) $(zncdir)

7
plugins/znc/Makefile.am Normal file
View File

@ -0,0 +1,7 @@
libdir = $(hexchatlibdir)
lib_LTLIBRARIES = znc.la
znc_la_SOURCES = znc.c
znc_la_LDFLAGS = -avoid-version -module
znc_la_LIBADD =
AM_CPPFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/../../src/common

387
plugins/znc/znc.c Normal file
View File

@ -0,0 +1,387 @@
/* HexChat
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include "hexchat-plugin.h"
static hexchat_plugin *ph;
static char name[] = "ZNC Extras";
static char desc[] = "Integrate with optional ZNC plugins";
static char version[] = "1.0";
/* Playback: http://wiki.znc.in/Playback */
/* This should behave like this:
* On connect (end of MOTD):
* if new server, play all
* if old server, play all after latest timestamp
* On new query:
* play all
* On close query:
* clear all
* On new message:
* update latest timestamp
*/
static GHashTable *playback_servers = NULL;
#define PLAYBACK_CAP "znc.in/playback"
static int
get_current_context_type (void)
{
int type = 0;
hexchat_list *list;
hexchat_context *cur_ctx;
list = hexchat_list_get (ph, "channels");
if (!list)
return 0;
cur_ctx = hexchat_get_context (ph);
while (hexchat_list_next (ph, list))
{
if ((hexchat_context*)hexchat_list_str (ph, list, "context") == cur_ctx)
{
type = hexchat_list_int (ph, list, "type");
break;
}
}
hexchat_list_free (ph, list);
return type;
}
static int
get_current_context_id (void)
{
gint id;
if (hexchat_get_prefs (ph, "id", NULL, &id) != 2)
return -1;
return id;
}
static int
capls_cb (char *word[], void *userdata)
{
if (strstr (word[2], PLAYBACK_CAP) != NULL)
hexchat_command (ph, "quote CAP REQ :" PLAYBACK_CAP);
return HEXCHAT_EAT_NONE;
}
static int
capack_cb (char *word[], void *userdata)
{
if (strstr (word[2], PLAYBACK_CAP) != NULL)
{
int id;
/* FIXME: emit this after request but before ack.. */
hexchat_emit_print (ph, "Capability Request", PLAYBACK_CAP, NULL);
id = get_current_context_id ();
if (!g_hash_table_contains (playback_servers, GINT_TO_POINTER(id)))
g_hash_table_insert (playback_servers, GINT_TO_POINTER(id), g_strdup ("0"));
}
return HEXCHAT_EAT_NONE;
}
static int
connected_cb (char *word[], char *word_eol[], void *userdata)
{
int id = get_current_context_id ();
if (g_hash_table_contains (playback_servers, GINT_TO_POINTER(id)))
{
g_print("connected\n");
char *timestamp = g_hash_table_lookup (playback_servers, GINT_TO_POINTER(id));
hexchat_commandf (ph, "quote PRIVMSG *playback :play * %s", timestamp);
}
return HEXCHAT_EAT_NONE;
}
static int
opencontext_cb (char *word[], void *userdata)
{
#if 0
int id = get_current_context_id ();
if (g_hash_table_contains (playback_servers, GINT_TO_POINTER(id))
&& get_current_context_type () == 3)
{
const char *channel = hexchat_get_info (ph, "channel");
char *time_str = g_hash_table_lookup (playback_servers, GINT_TO_POINTER(id));
hexchat_commandf (ph, "quote PRIVMSG *playback :play %s %s", channel, time_str);
}
#endif
return HEXCHAT_EAT_NONE;
}
static int
closecontext_cb (char *word[], void *userdata)
{
int id = get_current_context_id ();
if (g_hash_table_contains (playback_servers, GINT_TO_POINTER(id)))
{
int type = get_current_context_type ();
if (type == 3) /* Dialog */
{
const char *channel = hexchat_get_info (ph, "channel");
hexchat_commandf (ph, "quote PRIVMSG *playback :clear %s", channel);
}
else if (type == 1) /* Server */
{
g_hash_table_remove (playback_servers, GINT_TO_POINTER(id));
}
}
return HEXCHAT_EAT_NONE;
}
static int
chanactivity_cb (char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
{
int id;
if (!attrs->server_time_utc)
return HEXCHAT_EAT_NONE;
id = get_current_context_id ();
if (g_hash_table_contains (playback_servers, GINT_TO_POINTER(id)))
{
char *time_str;
time_str = g_strdup_printf ("%ld", attrs->server_time_utc);
g_hash_table_replace (playback_servers, GINT_TO_POINTER(id), time_str);
}
return HEXCHAT_EAT_NONE;
}
/* End Playback */
/* Buffextras: http://wiki.znc.in/Buffextras */
static inline char *
strip_brackets (char *str)
{
str++;
str[strlen(str) - 1] = '\0';
return str;
}
static int
buffextras_cb (char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
{
char *channel, *user_nick, *user_host = NULL, *p;
if (g_ascii_strncasecmp (word[1] + 1, "*buffextras", 11) != 0)
return HEXCHAT_EAT_NONE;
channel = word[3];
user_nick = word[4] + 1;
p = strchr (user_nick, '!');
if (p != NULL)
{
*p = '\0';
user_host = p + 1;
}
/* This might be gross, but there are a lot of repeated calls.. */
#define EMIT(x,...) hexchat_emit_print_attrs(ph,attrs,x,__VA_ARGS__,NULL)
#define IS_EVENT(x) g_str_has_prefix(word_eol[5],x)
if (IS_EVENT ("joined"))
{
EMIT ("Join", user_nick, channel, user_host);
}
else if (IS_EVENT ("quit with message"))
{
EMIT ("Quit", user_nick, strip_brackets(word_eol[7]), user_host);
}
else if (IS_EVENT ("parted with message"))
{
char *reason = strip_brackets(word_eol[7]);
if (*reason)
EMIT ("Part with Reason", user_nick, user_host, channel, reason);
else
EMIT ("Part", user_nick, user_host, channel);
}
else if (IS_EVENT ("is now known as"))
{
EMIT ("Change Nick", user_nick, word[9]);
}
else if (IS_EVENT ("set mode"))
{
char *prefix = (word[7][0] == '+' ? "+" : "-");
EMIT ("Channel Mode Generic", user_nick, prefix, word[7] + 1, channel);
}
else if (IS_EVENT ("changed the topic to"))
{
EMIT ("Topic Change", user_nick, word_eol[9], channel);
}
else if (IS_EVENT ("kicked")) /* X Reason: [Y] */
{
EMIT ("Kick", user_nick, word[6], channel, strip_brackets(word_eol[8]));
}
else
{
return HEXCHAT_EAT_NONE; /* We don't know, let it print */
}
return HEXCHAT_EAT_ALL;
}
/* End Buffextras */
/* Privmsg: http://wiki.znc.in/Privmsg */
static gboolean
prefix_is_channel (char prefix)
{
gboolean is_channel = TRUE; /* Defaults to TRUE as being wrong would be annoying */
int id;
hexchat_list *list;
if (hexchat_get_prefs (ph, "id", NULL, &id) != 2)
return TRUE;
list = hexchat_list_get (ph, "channels");
if (!list)
return TRUE;
while (hexchat_list_next (ph, list))
{
if (hexchat_list_int (ph, list, "id") == id)
{
const char *chantypes = hexchat_list_str (ph, list, "chantypes");
if (strchr (chantypes, prefix) == NULL)
is_channel = FALSE;
break;
}
}
hexchat_list_free (ph, list);
return is_channel;
}
static int
privmsg_cb (char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
{
char *sender, *recipient, *msg, *p;
const char *network, *mynick;
if (prefix_is_channel (word[3][0]))
return HEXCHAT_EAT_NONE;
mynick = hexchat_get_info (ph, "nick");
network = hexchat_get_info (ph, "network");
recipient = word[3];
msg = word_eol[4] + 1;
sender = word[1] + 1;
p = strchr (sender, '!');
if (p != NULL)
*p = '\0';
if (hexchat_nickcmp (ph, sender, mynick) == 0
&& hexchat_nickcmp (ph, recipient, mynick) != 0)
{
hexchat_context *new_ctx;
char *event;
hexchat_commandf (ph, "query -nofocus %s", recipient);
new_ctx = hexchat_find_context (ph, network, recipient);
hexchat_set_context (ph, new_ctx);
if (g_ascii_strncasecmp (msg, "\001ACTION", 7) == 0)
{
msg += 7;
msg[strlen (msg) - 1] = '\0';
msg = g_strstrip (msg);
event = "Your Action";
}
else
{
event = "Your Message";
}
hexchat_emit_print_attrs (ph, attrs, event, mynick, msg, NULL);
return HEXCHAT_EAT_ALL;
}
return HEXCHAT_EAT_NONE;
}
/* End Privmsg */
int
hexchat_plugin_init (hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc,
char **plugin_version, char *arg)
{
ph = plugin_handle;
*plugin_name = name;
*plugin_desc = desc;
*plugin_version = version;
/* Privmsg */
hexchat_hook_server_attrs (ph, "PRIVMSG", HEXCHAT_PRI_HIGH, privmsg_cb, NULL);
/* Buffextras */
hexchat_hook_server_attrs (ph, "PRIVMSG", HEXCHAT_PRI_HIGHEST, buffextras_cb, NULL);
/* Playback */
playback_servers = g_hash_table_new_full (NULL, NULL, NULL, g_free);
hexchat_hook_print (ph, "Capability List", HEXCHAT_PRI_NORM, capls_cb, NULL);
hexchat_hook_print (ph, "Capability Acknowledgement", HEXCHAT_PRI_NORM, capack_cb, NULL);
hexchat_hook_print (ph, "Open Context", HEXCHAT_PRI_NORM, opencontext_cb, NULL);
hexchat_hook_print (ph, "Close Context", HEXCHAT_PRI_NORM, closecontext_cb, NULL);
hexchat_hook_server (ph, "376", HEXCHAT_PRI_NORM, connected_cb, NULL);
/* FIXME: Events buffextras uses */
hexchat_hook_server_attrs (ph, "PRIVMSG", HEXCHAT_PRI_LOW, chanactivity_cb, NULL);
hexchat_printf (ph, "%s plugin loaded\n", name);
return 1;
}
int
hexchat_plugin_deinit (void)
{
g_hash_table_unref (playback_servers);
hexchat_printf (ph, "%s plugin unloaded\n", name);
return 1;
}