[mw-devel] [Git][arthur/mw][master] New mwjs API

Andrew Price welshbyte at sucs.org
Fri Mar 16 19:57:09 GMT 2018


Andrew Price pushed to branch master at Justin Mitchell / mw


Commits:
76808c88 by Andrew Price at 2018-03-16T19:53:08+00:00
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);

- - - - -


7 changed files:

- + src/client/event.h
- src/client/incoming.c
- src/client/incoming.h
- src/client/js-duk.c
- src/client/js.h
- src/ipc.c
- src/ipc.h


Changes:

=====================================
src/client/event.h
=====================================
--- /dev/null
+++ b/src/client/event.h
@@ -0,0 +1,18 @@
+#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__ */


=====================================
src/client/incoming.c
=====================================
--- a/src/client/incoming.c
+++ b/src/client/incoming.c
@@ -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)


=====================================
src/client/incoming.h
=====================================
--- a/src/client/incoming.h
+++ b/src/client/incoming.h
@@ -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);


=====================================
src/client/js-duk.c
=====================================
--- a/src/client/js-duk.c
+++ b/src/client/js-duk.c
@@ -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);


=====================================
src/client/js.h
=====================================
--- a/src/client/js.h
+++ b/src/client/js.h
@@ -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,


=====================================
src/ipc.c
=====================================
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -16,6 +16,19 @@ char *ipc_host = NULL;
 char *ipc_port = NULL;
 struct user *ipc_user;
 
+const char *_plural_suffix[] = {
+	[0] = "",
+	[1] = "'s",
+	[2] = "'",
+	[3] = "'d",
+	[4] = "'ll",
+};
+const char **plural_suffix = _plural_suffix;
+int plural_suffix_size(void)
+{
+	return sizeof(_plural_suffix) / sizeof(_plural_suffix[0]);
+}
+
 const char *get_nonce(void);
 
 int ipc_connected(void)


=====================================
src/ipc.h
=====================================
--- a/src/ipc.h
+++ b/src/ipc.h
@@ -76,4 +76,7 @@ void ipcmsg_transmit(ipc_message_t * msg);
 int msg_queue(ipc_message_t *msg, ipc_connection_t *conn);
 const char * ipc_nametype(enum ipc_types msgtype);
 
+const char **plural_suffix;
+int plural_suffix_size(void);
+
 #endif



View it on GitLab: https://projects.sucs.org/arthur/mw/commit/76808c8847eb4f404ab0c6dbb49ce53be2b74830

---
View it on GitLab: https://projects.sucs.org/arthur/mw/commit/76808c8847eb4f404ab0c6dbb49ce53be2b74830
You're receiving this email because of your account on projects.sucs.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.sucs.org/pipermail/mw-devel/attachments/20180316/57a5c0c9/attachment-0001.html>


More information about the mw-devel mailing list