Commit 76808c88 authored by Andrew Price's avatar Andrew Price
Browse files

New mwjs API

- Better namespacing.
- Useful message objects.
- New generic event mechanism instead of just message received events.
- mw.onevent array of function refs instead of weird bind() calls:
  > mw.onevent.push(myhandler);
- Discoverable:
  > mw.print(Object.getOwnPropertyNames(mw));
  onevent,print,exec,say,wholist,urlget,beep,input,termsize

Example:

  function handler(ev)
  {
        if (ev.type == "message_received") {
                msg = ev.data;
                // Some fields only defined for certain message types
                mw.print("msg.text: " + msg.text);
                mw.print("msg.unixtime: " + msg.unixtime);
                mw.print("msg.serial: " + msg.serial);
                mw.print("msg.ipc_type: " + msg.ipc_type);
                mw.print("msg.from_name: " + msg.from_name);
                mw.print("msg.to_name: " + msg.to_name);
                mw.print("msg.type: " + msg.type);
                mw.print("msg.excluded_name: " + msg.excluded_name);
                mw.print("msg.suffix: " + msg.suffix);
        }
  }
  mw.onevent.push(handler);
parent dd30057e
Loading
Loading
Loading
Loading
Loading

src/client/event.h

0 → 100644
+18 −0
Original line number Diff line number Diff line
#ifndef __EVENT_H__
#define __EVENT_H__

#include <socket.h>

typedef enum {
	MWEV_TYPE_NONE = 0,
	MWEV_TYPE_MSG,
} mwev_type_t;

struct mwevent {
	mwev_type_t ev_type;
	union {
		ipc_message_t *msg; /* MWEV_TYPE_MSG */
	} ev_data;
};

#endif /* __EVENT_H__ */
+36 −53
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@
#include "util.h"
#include "incoming.h"
#include "gaglist.h"
#include "js.h"
#include "event.h"
#include <jansson.h>
#include <str_util.h>

@@ -41,7 +43,6 @@ int mesg_waiting = 0;
char *mrod_user=NULL;

static int MesgStacked=0;
static int events_cancelled = 0;
static struct mstack *MesgStack=NULL;

static void accept_pipe_cmd(ipc_message_t *msg, struct user *mesg_user);
@@ -64,38 +65,20 @@ static void force_protpower(char *text);

#define _MIN(a,b) (a<b)?a:b

void InsertMesg(struct mstack *new)
static void InsertMesg(struct mstack *new, ipc_message_t *msg)
{
	struct mstack **mp = &MesgStack;

	while (*mp != NULL)
		mp = &(*mp)->next;

	new->next=NULL;
	if (MesgStack==NULL)
	{
		MesgStack=new;
		MesgStacked=1;
		events_cancelled = 0;
	}else
	{
		struct mstack *ptr;
		ptr=MesgStack;
		while (ptr->next!=NULL) ptr=ptr->next;
		ptr->next=new;
		/* Stack must be emptied before events can start again */
		if (events_cancelled)
			ptr->flags &= ~MST_EVENT;
		MesgStacked++;
	}
}
	new->msg = msg;
	if (msg != NULL)
		msg->refcount++;

void StackMesg(char *text, char *from, int flags)
{
	struct mstack *new;
	new=(struct mstack *)malloc(sizeof(struct mstack));
	new->text=(char *)malloc(strlen(text)+1);
	new->from=(char *)malloc(strlen(from)+1);
	strcpy(new->text,text);
	strcpy(new->from,from);
	new->flags = flags;
	new->preamble = 0;
	InsertMesg(new);
	*mp = new;
	MesgStacked++;
}

static void StackEvent(char *text, char *from, int flags)
@@ -108,7 +91,7 @@ static void StackEvent(char *text, char *from, int flags)
	strcpy(new->from,from);
	new->flags = MST_SCREV;
	new->preamble = flags;
	InsertMesg(new);
	InsertMesg(new, NULL);
}

void ClearStack(void) {
@@ -118,6 +101,8 @@ void ClearStack(void) {
	while (MesgStack!=NULL) {
		old=MesgStack;
		MesgStack=old->next;
		if (old->msg)
			ipcmsg_destroy(old->msg);
		free(old->text);
		free(old->from);
		free(old);
@@ -142,6 +127,11 @@ void DisplayStack(void)
				script_output=1;
				while ((event_name = NextLink(event_list, event_name)) != NULL)
				{
					struct mwevent ev = {
						.ev_type = MWEV_TYPE_MSG,
						.ev_data.msg = new->msg,
					};
					js_handle_event(&ev);
					ExecEvent(event_name, new->text, "text", new->from, new->preamble);
				}
				if (script_output) display_message(new->text, new->flags & MST_BEEP, 1);
@@ -559,24 +549,11 @@ static void display_content(ipc_message_t *msg)
			snprintf(tb, len, "%s", text);
		} else
		if (strcmp(type, "emote")==0) {
			int plural = json_getint(j, "plural");
			switch (plural) {
				case 1:
					snprintf(tb, len, "%s's %s", whom, text);
					break;
				case 2:
					snprintf(tb, len, "%s' %s", whom, text);
					break;
				case 3:
					snprintf(tb, len, "%s'd %s", whom, text);
					break;
				case 4:
					snprintf(tb, len, "%s'll %s", whom, text);
					break;
				default:
					snprintf(tb, len, "%s %s", whom, text);
					break;
			}
			unsigned p = json_getint(j, "plural");

			if (p >= plural_suffix_size())
				p = 0;
			snprintf(tb, len, "%s%s %s", whom, plural_suffix[p], text);
		} else
		if (strcmp(type, "notsayto")==0) {
			const char *exclude = json_getstring(j, "exclude");
@@ -851,15 +828,19 @@ static void force_text(ipc_message_t *msg, const char *text, const char *from)
	mesg->text = strdup(tb);
	mesg->from = strdup(from);

	/*printf("\n<Received message from %s: \'%s\'>\n", from, text);*/
	InsertMesg(mesg);
	InsertMesg(mesg, msg);
}

static void force_wiz(ipc_message_t *msg, char *newbuff, char *from)
{
	char tb[MAXTEXTLENGTH];
	tb[0]=0;
	struct mstack *mesg;

	mesg = malloc(sizeof(*mesg));
	mesg->flags = 0;
	mesg->preamble = 0;

	tb[0]=0;
	if (s_timestamp(user))
	{
		time_t t;
@@ -875,8 +856,10 @@ static void force_wiz(ipc_message_t *msg, char *newbuff, char *from)
		strncat(tb, "> ", MAXTEXTLENGTH - strlen(tb) - 1);
	}
	strncat(tb, newbuff, MAXTEXTLENGTH - strlen(tb) - 1);
	mesg->text = strdup(tb);
	mesg->from = strdup(from);

	StackMesg(tb, from, 0);
	InsertMesg(mesg, msg);
}

static void force_checkonoff(char *text, char *from)
+2 −2
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ struct mstack
	int flags;
	int preamble; /* Extra chars added by global/timestamp/spy */
	struct mstack *next;
	ipc_message_t *msg;
	struct user *sender;
};

extern int new_mail_waiting;
@@ -29,8 +31,6 @@ extern char *mrod_user;

void incoming_mesg(int ignore);
void handle_mesg(void);
void InsertMesg(struct mstack *mesg);
void StackMesg(char *text, char *from, int event);
extern int MesgIsStacked(void);
void DisplayStack(void);
void ClearStack(void);
+175 −7
Original line number Diff line number Diff line
@@ -135,6 +135,70 @@ static char *cesu8_to_utf8(const char *cesu8)
	return utf8;
}

static int js_push_username(int32_t id)
{
	struct user u;
	int ret;

	ret = fetch_user(&u, id);
	if (ret == 0)
		duk_push_string(ctx, u.record.name);
	else
		duk_push_undefined(ctx);
	return ret;
}

static int js_push_ipcmsg_event(ipc_message_t *msg)
{
	duk_idx_t idx;
	json_t *json;
	json_t *val;

	/* Only support json-formatted messages */
	if (msg == NULL || msg->head.type <= 26)
		return 1;

	json = ipcmsg_json_decode(msg);
	if (json == NULL) {
		fprintf(stderr, "mwjs error: failed to unmarshall message\n");
		return -1;
	}
	idx = duk_push_bare_object(ctx); /* msg object */

	/* The message object will have common members which are set from the
	 * msg header and the rest (json encoded) will be dependent on the
	 * message type.
	 */
	duk_push_number(ctx, msg->head.when);
	duk_put_prop_string(ctx, idx, "unixtime");
	duk_push_number(ctx, msg->head.serial);
	duk_put_prop_string(ctx, idx, "serial");
	duk_push_string(ctx, ipc_nametype(msg->head.type));
	duk_put_prop_string(ctx, idx, "ipc_type");
	js_push_username(msg->head.src);
	duk_put_prop_string(ctx, idx, "from_name");
	js_push_username(msg->head.dst);
	duk_put_prop_string(ctx, idx, "to_name");

	duk_push_string(ctx, json_getstring(json, "type"));
	duk_put_prop_string(ctx, idx, "type");
	duk_push_string(ctx, json_getstring(json, "text"));
	duk_put_prop_string(ctx, idx, "text");
	duk_push_string(ctx, json_getstring(json, "exclude"));
	duk_put_prop_string(ctx, idx, "excluded_name");

	val = json_object_get(json, "plural");
	if (val != NULL && json_is_integer(val)) {
		unsigned p = json_integer_value(val);

		if (p >= plural_suffix_size())
			p = 0;
		duk_push_string(ctx, plural_suffix[p]);
		duk_put_prop_string(ctx, idx, "suffix");
	}
	return 0;
}

static duk_ret_t js_print(duk_context *cx)
{
	int argc = duk_get_top(cx);
@@ -621,15 +685,9 @@ int js_isrunning(void)
	return (interrupt == 0 && timeout_event != NULL);
}

int js_exec(char *name, int argc, const char **argv)
static int mwjs_call_fn(int argc)
{
	duk_int_t ret;
	int i;

	if (!duk_get_global_string(ctx, name) || !duk_is_function(ctx, -1))
		return 0;
	for (i = 0; i < argc; i++)
		duk_push_string(ctx, argv[i]);

	interrupt = 0;
	start_timeout();
@@ -649,6 +707,88 @@ int js_exec(char *name, int argc, const char **argv)
		script_output = 0;
	}
	duk_pop(ctx);
	return (ret == DUK_EXEC_SUCCESS) ? 0 : -1;
}

int js_exec(char *name, int argc, const char **argv)
{
	int i;

	if (!duk_get_global_string(ctx, name) || !duk_is_function(ctx, -1))
		return 0;
	for (i = 0; i < argc; i++)
		duk_push_string(ctx, argv[i]);

	(void)mwjs_call_fn(argc);
	return 0;
}

static int js_push_event(struct mwevent *ev)
{
	duk_idx_t idx = duk_push_bare_object(ctx);
	int ret = -1;

	/* Although incoming IPC messages are the only kind of event we
	 * currently handle, the message is set as a member of an event object
	 * and the event handler is passed the event object. This allows us to
	 * extend this mechanism in future to handle other kinds of client
	 * events.
	 */
	switch (ev->ev_type) {
	case MWEV_TYPE_MSG:
		duk_push_string(ctx, "message_received");
		duk_put_prop_string(ctx, idx, "type");
		ret = js_push_ipcmsg_event(ev->ev_data.msg);
		if (ret == 0)
			duk_put_prop_string(ctx, idx, "data");
		break;
	case MWEV_TYPE_NONE:
		/* Fall through */
	default:
		fprintf(stderr, "mwjs warning: Unhandled event type: %d\n", ev->ev_type);
		duk_pop(ctx);
		break;
	}
	if (ret != 0)
		duk_pop(ctx);
	return ret;
}

int js_handle_event(struct mwevent *ev)
{
	unsigned len;
	unsigned i;

	if (!duk_get_global_string(ctx, "mw")) {
		fprintf(stderr, "mwjs error: failed to lookup mw namespace\n");
		return -1;
	}
	if (!duk_get_prop_string(ctx, -1, "onevent")) {
		fprintf(stderr, "mwjs error: failed to lookup 'mw.onevent'\n");
		return -1;
	}
	if (!duk_is_array(ctx, -1)) {
		fprintf(stderr, "mwjs error: 'mw.onevent' is not an array\n");
		return -1;
	}
	len = duk_get_length(ctx, -1);
	if (len == 0)
		return 0;

	for (i = 0; i < len; i++) {
		duk_get_prop_index(ctx, -1, i);
		if (!duk_is_function(ctx, -1)) {
			duk_int_t t = duk_get_type(ctx, -1);
			fprintf(stderr, "mwjs warning: mw.onevent[%u] is of type %d, "
			                "not a function\n", i, t);
			continue;
		}
		if (js_push_event(ev) != 0) {
			duk_pop(ctx); /* Pop the function */
			continue;
		}
		mwjs_call_fn(1); /* Pops the return value */
	}
	return 0;
}

@@ -798,6 +938,30 @@ static void define_constants(void)
	define_string("whoami", user->record.name);
}

static void define_api(void)
{
	duk_push_string(ctx, "mw");
	duk_push_bare_object(ctx);

	/* mw.onevent = new Array(); */
	duk_push_string(ctx, "onevent");
	duk_push_array(ctx);
	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);

	/* Define values under the 'mw' namespace */
	define_func("print", js_print, DUK_VARARGS);
	define_func("exec", js_mwexec, 1);
	define_func("say", js_say, 1);
	define_func("wholist", js_wholist, 0);
	define_func("urlget", js_urlget, 1);
	define_func("beep", js_beep, 1);
	define_func("input", js_input, 1);
	define_func("termsize", js_termsize, 0);

	/* mw object */
	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
}

int setup_js(void)
{
	int is_local = 1;
@@ -810,6 +974,10 @@ int setup_js(void)
		return -1;

	duk_push_global_object(ctx);
	/* New API, namespaced under 'mw' */
	define_api();

	/* Old mwscripty API */
	define_constants();
	define_func("print", js_print, DUK_VARARGS);
	define_func("exec", js_mwexec, 1);
+2 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
#define JS_H

#include <stdio.h>
#include "event.h"

int js_isrunning(void);
int js_exec(char *name, int argc, const char **argvc);
@@ -10,6 +11,7 @@ int is_js(char *name);
void js_stop_execution(void);
int stop_js(void);
int setup_js(void);
int js_handle_event(struct mwevent *ev);

enum bindtype {
	K_BIND = 0,
Loading