[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