C/C++ public triggers?

Discussion about Perl, TCL or Python scripts and C/C++ plugins (using and writing them).

C/C++ public trigger

Postby KD0BPV » 13 Nov 2007 06:11

Hey, I'm new to Xchat, and I'm trying to figure out the scripting languages. I'm most familiar with C/C++ since i've written a few programs before in that language.

What I'm wondering is: How can I do public triggers in XChat via C/C++.

The desired effect would be for my plugin to respond when someone types a command like !help. I know that in mIRC, it would be equivilent to
Code: Select all
on *:TEXT:!help*:#: {

<command set>

}

KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby 2Points » 13 Nov 2007 17:11

First of all, you might want to look at the X-Chat C plugin interface, which is located here.

I've hacked together a little sample script, this should get you started with the most basic things. There's information on how to compile plugins on win32 in the documentation, on Linux 'gcc -Os -shared -o plugin.so plugin.c' seems to work fine.

Code: Select all
#include <xchat/xchat-plugin.h>
#include <string.h>

static xchat_plugin* ph;   // X-Chat plugin handle

// This would be your message callback
static int message_cb(char* word[], void* userdata)
{
   const char* channel = xchat_get_info(ph, "channel");

   // Nickname of the person who sent this message
   xchat_printf(ph, "Nickname: %s", word[1]);
   // His message
   xchat_printf(ph, "Text: %s", word[2]);
   // His user mode on the channel
   xchat_printf(ph, "Mode char: %s", word[3]);
   // Channel this callback was activated in
   xchat_printf(ph, "Channel: %s", channel);

   // If '@A' wrote 'test 123' in a channel '#opers',
   // this function would print:
   //  Nickname: A
    //  Text: test 123
    //  Mode char: @
   //  Channel: #opers
   //
   //  To do anything useful, you'd have to evaluate the text
   //  (e.g. word[2]) and the channel and act accordingly.
   //  Simple sample:
   
   if (strcmp(word[2], "!help") == 0) {
      // Notice how you must strip any color codes
      // X-Chat might have added to the nickname before you
      // can use it in commands

      // 1 - Strip mIRC colors
      // 2 - Strip text attributes
      // At least that's what the reference sample claims
      const int flags = 1 | 2;
      char* nickname = xchat_strip(ph, word[1], -1, flags);

      xchat_commandf(ph, "NOTICE %s Hello %s, welcome to channel %s.",
            nickname, nickname, channel);

      // Free string allocated by xchat_strip
      xchat_free(ph, nickname);
   }
   // Let X-Chat process this event further - if you return
   // XCHAT_EAT_ALL here, you wouldn't see any message anymore
   return XCHAT_EAT_NONE;
}

int xchat_plugin_init(xchat_plugin *plugin_handle,
                      char **plugin_name,
                      char **plugin_desc,
                      char **plugin_version,
                      char *arg)
{
   // Save plugin handle
   ph = plugin_handle;

   // Plugin information used by X-Chat
   *plugin_name = "Plugin name";
   *plugin_desc = "Plugin description";
   *plugin_version = "Plugin version";

   // Hook 'Channel Message' event to your function
   xchat_hook_print(ph, "Channel Message", XCHAT_PRI_NORM, message_cb, 0);

   return 1; // 1: Plugin initialization successful
}


A list of hookable print events and their respective arguments in the word-array can be found in X-Chat under Settings->Advanced->Text events.
User avatar
2Points
 
Posts: 173
Joined: 21 Dec 2006 18:49

Postby KD0BPV » 17 Nov 2007 06:48

Excellent. I'll give this a try when I feel like programming agian. Thank you :)
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby KD0BPV » 19 Nov 2007 06:49

hmm. My plug in is compiling just fine, but XChat says " No xchat_plugin_init symbol; is this really an xchat plugin?" even though I have that function defined.

And where am I supposed to have plugin.def?

I'm running windows, and I'm using Dev-C++ from bloodshed.net

Code: Select all
int xchat_plugin_init(xchat_plugin *handle, char **name, char **desc, char **ver, char *arg)
{

   // save the plugin handle and information
   ph = handle;
   *name = "Ham Trivia bot";
   *desc = "This bot provides trivia functionality for #fridaynightroundtable.net and #wd0hwt";
   *ver = "v1.0.0";

   // hook the channel message event to message_cb()
   xchat_hook_print(ph, "Channel Message", XCHAT_PRI_NORM, message_cb, 0);

   // Return successful initialization
   return 1;
}
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby peterz » 19 Nov 2007 22:55

KD0BPV wrote:hmm. My plug in is compiling just fine, but XChat says " No xchat_plugin_init symbol; is this really an xchat plugin?" even though I have that function defined.

And where am I supposed to have plugin.def?
[/code]


If you're using C++, you need to put xchat_plugin_init inside a extern "C" { } to avoid symbol decoration. plugin.def is needed in the current dir.
http://xchat.org/docs/plugin20.html#win32
User avatar
peterz
 
Posts: 1035
Joined: 09 Jun 2004 13:51
Location: Australia

Postby KD0BPV » 20 Nov 2007 02:18

Ok, well, I got the plugin loading now, but xchat crashes when my it is triggered. I suspect that It might be something to do with my nickstrip() function. I'm going to try a few ideas with that, but any suggestions would be helpful.

here's the entire source code

Code: Select all
#include <xchat-plugin.h>
#include <string.h>

#define PNAME "Ham Trivia bot"
#define PDESC "This bot provides trivia functionality for #fridaynightroundtable.net and #wd0hwt"
#define PVERSION "v1.0.0"

static xchat_plugin *ph; // X-Chat Plugin handle

// Function for stripping color codes, etc from nicks
char* nickstrip(char* nick, xchat_plugin* ph)
{
      const int flags = 1 | 2;
      char* anick = xchat_strip(ph, nick, -1, flags);
      char* bnick = anick;

      xchat_free(ph, anick);

      return bnick;
}

// Message Callback
static int message_cb(char* word[], void* userdata)
{
   const char* channel = xchat_get_info(ph, "channel");
       
   if (strcmp(word[2], "!test") == 0)
   {
      char* nick = nickstrip(word[1], ph);

      xchat_commandf(ph, "MSG %s Hey %s, your test is successfull.", nick, nick);
      xchat_free(ph, nick);
   }

return XCHAT_EAT_NONE;
}

void xchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
{
   *name = PNAME;
   *desc = PDESC;
   *version = PVERSION;
}

// Initialize the plugin
int xchat_plugin_init(xchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
{

   // save the plugin handle and information
   ph = plugin_handle;
   *plugin_name = PNAME;
   *plugin_desc = PDESC;
   *plugin_version = PVERSION;

   // hook the channel message event to message_cb()
   xchat_hook_print(ph, "Channel Message", XCHAT_PRI_NORM, message_cb, 0);

   // Return successful initialization
   return 1;
}
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby KD0BPV » 20 Nov 2007 04:47

I seem to have tracked this down to an issue reading the word[] and word_eol[] arrays. I tried setting them to standard variables, but my compiler gives me several errors about typecasting.

The part I don't get is that my if statement is reading the variable just fine, but once I try to use it after that, xchat crashes.

Any ideas?
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby 2Points » 20 Nov 2007 13:46

Code: Select all
// Function for stripping color codes, etc from nicks
char* nickstrip(char* nick, xchat_plugin* ph)
{
      const int flags = 1 | 2;
      char* anick = xchat_strip(ph, nick, -1, flags);
      char* bnick = anick;

      xchat_free(ph, anick);

      return bnick;
}

Consider this - you assign bnick to anick, that is, bnick is pointing to the same address you free in the next line with xchat_free. You're essentially returning already freed memory, plus freeing it one more time after xchat_commandf. Memory access fault is inevitable.

Just remove the bnick and xchat_free parts from nickstrip, return anick instead and free it after xchat_commandf in your message callback.
User avatar
2Points
 
Posts: 173
Joined: 21 Dec 2006 18:49

Postby KD0BPV » 20 Nov 2007 17:31

excellent. that got my nickstrip function working, but now I have 2 more problems:

1. my plugin doesn't trigger when more than the trigger is used, IE if someone sends "!test" my plugin sees it and processes it, if they send "!test testing 1 2 3" my plugin doesn't even respond.

2. reading nick and word[] seem to be working, but not word_eol[] which I did add to the prototype of message_cb() before trying to use it.

prototype:
static int message_cb(char* word[], char* word_eol[], void *userdata)
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby 2Points » 20 Nov 2007 20:12

1. Indeed, strcmp() is not really suited for this kind of task. I'm not too familiar with the C standard library's string functions, but you could try strstr(), strtok() or even the regular expressions library. Or any string library, for that matter.

2. From what I understand, word_eol isn't available for print hooks, only for command and server hooks (the prototype for xchat_hook_print expects a function such as: int (*callback) (char *word[], void *user_data)). You'll have to split word[2] yourself if you need this functionality.

You could try something like this:

Code: Select all
// strstr() returns a pointer to the first character
// of substring "!trigger" in a string, or NULL
// if no match is found.
// Thus, if strstr returns a pointer that is identical to
// the passed string, the string starts with "!trigger"
if (strstr(word[2], "!trigger") == word[2]) {
   // Since the word array mustn't be modified, copy the string
   char* string = strdup(word[2]);

   char* token = strtok(string, " ");
   do {
      // You could do something useful with the tokens here,
      // like store or parse them. (Copy them with strdup if you want to store them)
      xchat_printf(ph, "Token: %s\n", token);
   } while (token = strtok(0, " "));

   // If the string was "!trigger abc 123", this would print:
   // Token: !trigger
   // Token: abc
   // Token: 123

   // Free the copied string
   free(string);
}


As you might see, this is becoming rather excessive for simple trigger scripting, which is why most people prefer more 'straight-forward' languages to write X-Chat plugins with. ;)
Anyway, using C++ standard library containers such as string and vector greatly simplifies string memory management and storage, so you should consider using those.
User avatar
2Points
 
Posts: 173
Joined: 21 Dec 2006 18:49

Postby KD0BPV » 21 Nov 2007 03:55

Ok, well, wouldn't a function to parse word[] and from that create word_eol[] work? I think I have an idea for it, but I would need to know how to find the highest index value of word[], and I would need to know how to concatenate strings in C, I think that can be done with strcat() as long as I include string.h, right?

does anyone know if they plan to add word_eol[] to print hooks anytime soon?

As for why I don't use pearl, python, or tcl is because I don't care to learn another language right now. I still find myself using PHP inside C/C++ or vice versa, and it's annoying. lol
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby 2Points » 21 Nov 2007 08:40

If you're in need of a reference, the GNU C Library Manual is a good place to start. Some of it only applies to GCC, but those parts are usually explicitly marked. String functions are here.
User avatar
2Points
 
Posts: 173
Joined: 21 Dec 2006 18:49

Postby KD0BPV » 26 Nov 2007 09:55

Those links did help. Thank you for those 2Points.

I'm down to one problem it would seem.

on line 61, my compiler says "invalid initializer"

here's my source at present, an I've added an ALL CAPS comment just above the troublesome line 61.

Code: Select all
#include <xchat-plugin.h>
#include <string.h>

#define PNAME "Ham Trivia bot"
#define PDESC "This bot provides trivia functionality for #fridaynightroundtable.net and #wd0hwt"
#define PVERSION "v0.0.3"

static xchat_plugin *ph; // X-Chat Plugin handle

// Function for parsing word[] to make word_eol[]
char* make_eol(char* source[])
{

   int word_index = 0;
   while (source[word_index])
   {
      word_index++;
   }

   char *eol[word_index];
   int indexa = 0;
   int indexb = 0;

   while (indexb << word_index)
   {

      while (indexa << word_index)
      {

         eol[indexb] = strcat(eol[indexb], source[indexa]);
         indexa++;

      }

      indexb++;
      indexa = 0;
   }

   return *eol;

}

// Function for stripping color codes, etc from nicks
char* nickstrip(char* nick, xchat_plugin* ph)
{

      char* anick = xchat_strip(ph, nick, -1, 1 | 2);

      return anick;
}

// Message Callback
static int message_cb(char* word[], void *userdata)
{

   const char* channel = xchat_get_info(ph, "channel");
       
   if (strstr(word[2], "!test") == word[2])
   {

                // HERE'S LINE 61!!!!
      char word_eol[32] = make_eol(word);
      char *nick = nickstrip(word[1], ph);

      xchat_commandf(ph, "MSG %s Hey %s, your test is successfull. You typed: %s", nick, nick, word_eol[2]);
      xchat_free(ph, nick);
   }

return XCHAT_EAT_NONE;
}

void xchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
{
   *name = PNAME;
   *desc = PDESC;
   *version = PVERSION;
}

// Initialize the plugin
int xchat_plugin_init(xchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
{

   // save the plugin handle and information
   ph = plugin_handle;
   *plugin_name = PNAME;
   *plugin_desc = PDESC;
   *plugin_version = PVERSION;

   // hook the channel message event to message_cb()
   xchat_hook_print(ph, "Channel Message", XCHAT_PRI_NORM, message_cb, 0);

   // Return successful initialization
   xchat_print(ph, "Ham Trivia Bot loaded!\n");
   return 1;
}
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby KD0BPV » 27 Nov 2007 16:25

ok, I found the problem causing the invalid initialization. now make_eol() is returning a memory address instead of the new array of strings though.

the new call to the function is:
Code: Select all
char* word_eol = make_eol(word);


and if I make word_eol a regular char, I get these compiler errors:

passing arg 1 of strcat() makes integer from pointer without a cast, Line 30: eol[indexb] = strcat(eol[indexb], &source[indexa]);

assignment makes integer from pointer without a cast, Line 30: eol[indexb] = strcat(eol[indexb], &source[indexa]);
KD0BPV
 
Posts: 18
Joined: 13 Nov 2007 06:02

Postby 2Points » 27 Nov 2007 18:28

No matter what you're trying to return in make_eol, it will always be invalid as long as you're not allocating the memory to hold the array and the strings with malloc (and subsequently free it with free). Memory on the stack will always be freed after the function call.

Also, << is binary left shift, not smaller than; you probably want to use < or <=.
User avatar
2Points
 
Posts: 173
Joined: 21 Dec 2006 18:49

Next

Return to Scripts and Plugins

Who is online

Users browsing this forum: No registered users and 1 guest