diff --git a/libdc/include/dc/api.h b/libdc/include/dc/api.h index 5a90fd9..5a826e2 100644 --- a/libdc/include/dc/api.h +++ b/libdc/include/dc/api.h @@ -90,6 +90,12 @@ bool dc_api_create_channel(dc_api_t api, dc_account_t login, */ bool dc_api_get_messages(dc_api_t api, dc_account_t login, dc_channel_t c); +/** + * post a message to the given channel + */ +bool dc_api_post_message(dc_api_t api, dc_account_t login, + dc_channel_t c, dc_message_t m); + /** * Fetch a list of friends of the login account "login". The friends are stored * within the login object. diff --git a/libdc/include/dc/message.h b/libdc/include/dc/message.h index 2555ee0..52abe01 100644 --- a/libdc/include/dc/message.h +++ b/libdc/include/dc/message.h @@ -10,6 +10,7 @@ struct dc_message_; typedef struct dc_message_ *dc_message_t; dc_message_t dc_message_new(void); +dc_message_t dc_message_new_content(char const *s, int len); dc_message_t dc_message_from_json(json_t *j); json_t *dc_message_to_json(dc_message_t m); diff --git a/libdc/include/dc/session.h b/libdc/include/dc/session.h index c175892..76f78c5 100644 --- a/libdc/include/dc/session.h +++ b/libdc/include/dc/session.h @@ -52,6 +52,12 @@ bool dc_session_has_token(dc_session_t s); */ dc_account_t dc_session_me(dc_session_t s); +/** + * Return the API handle in use by the session. Do not unref the reference + * and if you need it for something else, dc_ref() it yourself. + */ +dc_api_t dc_session_api(dc_session_t s); + /** * access to the internal account cache */ diff --git a/libdc/src/api-channel.c b/libdc/src/api-channel.c index f4fbb68..d84c40b 100644 --- a/libdc/src/api-channel.c +++ b/libdc/src/api-channel.c @@ -2,6 +2,36 @@ #include "internal.h" +bool dc_api_post_message(dc_api_t api, dc_account_t login, + dc_channel_t c, dc_message_t m) +{ + bool ret = false; + char *url = NULL; + json_t *j = NULL, *reply = NULL; + + return_if_true(api == NULL || login == NULL || m == NULL, false); + return_if_true(dc_message_content(m) == NULL, false); + + asprintf(&url, "channels/%s/messages", dc_channel_id(c)); + goto_if_true(url == NULL, cleanup); + + j = dc_message_to_json(m); + goto_if_true(j == NULL, cleanup); + + reply = dc_api_call_sync(api, "POST", TOKEN(login), url, j); + goto_if_true(reply != NULL, cleanup); + + ret = true; + +cleanup: + + free(url); + json_decref(j); + json_decref(reply); + + return ret; +} + bool dc_api_get_messages(dc_api_t api, dc_account_t login, dc_channel_t c) { bool ret = false; diff --git a/libdc/src/message.c b/libdc/src/message.c index b9d41bc..b9dd577 100644 --- a/libdc/src/message.c +++ b/libdc/src/message.c @@ -40,6 +40,24 @@ dc_message_t dc_message_new(void) return dc_ref(m); } +dc_message_t dc_message_new_content(char const *s, int len) +{ + dc_message_t m = dc_message_new(); + return_if_true(m == NULL, NULL); + + if (len < 0) { + len = strlen(s); + } + + m->content = strndup(s, len); + if (m->content == NULL) { + dc_unref(m); + return NULL; + } + + return m; +} + dc_message_t dc_message_from_json(json_t *j) { dc_message_t m = NULL; diff --git a/libdc/src/session.c b/libdc/src/session.c index b4d74cc..def3dd2 100644 --- a/libdc/src/session.c +++ b/libdc/src/session.c @@ -227,6 +227,12 @@ bool dc_session_has_token(dc_session_t s) return dc_account_has_token(s->login); } +dc_api_t dc_session_api(dc_session_t s) +{ + return_if_true(s == NULL, NULL); + return s->api; +} + dc_account_t dc_session_me(dc_session_t s) { return_if_true(s == NULL, NULL); diff --git a/ncdc/CMakeLists.txt b/ncdc/CMakeLists.txt index bc88a68..5ccfd6d 100644 --- a/ncdc/CMakeLists.txt +++ b/ncdc/CMakeLists.txt @@ -23,6 +23,7 @@ SET(SOURCES "src/mainwindow.c" "src/msg.c" "src/ncdc.c" + "src/post.c" "src/textview.c" "src/util.c" ) diff --git a/ncdc/include/ncdc/cmds.h b/ncdc/include/ncdc/cmds.h index e8bec5f..6a1f5fe 100644 --- a/ncdc/include/ncdc/cmds.h +++ b/ncdc/include/ncdc/cmds.h @@ -4,8 +4,14 @@ #include #include +/** + * n .. the main window handle + * ac,av .. the full string parsed up along white spaces + * f .. the full string, without the /command bit + */ typedef bool (*ncdc_command_t)(ncdc_mainwindow_t n, - size_t argc, wchar_t **argv); + size_t argc, wchar_t **argv, + wchar_t const *f); typedef struct { wchar_t const *name; @@ -24,10 +30,11 @@ bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s); */ ncdc_commands_t *ncdc_find_cmd(ncdc_commands_t *cmds, wchar_t const *name); -bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av); -bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av); -bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, wchar_t **av); -bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av); -bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av); +bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); +bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); +bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); +bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); +bool ncdc_cmd_post(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); +bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av, wchar_t const *f); #endif diff --git a/ncdc/include/ncdc/mainwindow.h b/ncdc/include/ncdc/mainwindow.h index 6c939fa..d92b8de 100644 --- a/ncdc/include/ncdc/mainwindow.h +++ b/ncdc/include/ncdc/mainwindow.h @@ -15,6 +15,7 @@ ncdc_mainwindow_t ncdc_mainwindow_new(void); void ncdc_mainwindow_log(ncdc_mainwindow_t w, wchar_t const *fmt, ...); GPtrArray *ncdc_mainwindow_views(ncdc_mainwindow_t n); +dc_channel_t ncdc_mainwindow_current_channel(ncdc_mainwindow_t n); void ncdc_mainwindow_switchview(ncdc_mainwindow_t n, int idx); void ncdc_mainwindow_refresh(ncdc_mainwindow_t n); diff --git a/ncdc/include/ncdc/ncdc.h b/ncdc/include/ncdc/ncdc.h index 72a9294..1b2f468 100644 --- a/ncdc/include/ncdc/ncdc.h +++ b/ncdc/include/ncdc/ncdc.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/ncdc/src/cmds.c b/ncdc/src/cmds.c index 46b803f..7628824 100644 --- a/ncdc/src/cmds.c +++ b/ncdc/src/cmds.c @@ -6,6 +6,7 @@ ncdc_commands_t cmds[] = { { L"/login", ncdc_cmd_login }, { L"/logout", ncdc_cmd_logout }, { L"/msg", ncdc_cmd_msg }, + { L"/post", ncdc_cmd_post }, { L"/quit", ncdc_cmd_quit }, { NULL, NULL } }; @@ -17,6 +18,7 @@ static pthread_cond_t cnd; typedef struct { ncdc_commands_t *cmd; + wchar_t *f; size_t ac; wchar_t **av; ncdc_mainwindow_t mainwindow; @@ -43,9 +45,12 @@ static void *async_dispatcher(void *arg) } else { /* call the handler */ - item->cmd->handler(item->mainwindow, item->ac, item->av); + item->cmd->handler(item->mainwindow, item->ac, + item->av, item->f + ); w_strfreev(item->av); + free(item->f); free(item); } } @@ -109,6 +114,8 @@ bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s) wchar_t **tokens = NULL; ncdc_commands_t *it = NULL; queue_item *item = NULL; + wchar_t *f = NULL; + size_t len = 0, cmdlen = 0; tokens = w_tokenise(s); return_if_true(tokens == NULL, false); @@ -121,12 +128,21 @@ bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s) return false; } + /* make a complete string without the /command part + */ + len = wcslen(s); + cmdlen = wcslen(it->name); + f = wcsdup(s); + return_if_true(f == NULL, false); + memmove(f, f+cmdlen, len-cmdlen); + item = calloc(1, sizeof(queue_item)); item->ac = w_strlenv(tokens); item->av = tokens; item->cmd = it; item->mainwindow = n; + item->f = f; pthread_mutex_lock(&mtx); g_queue_push_tail(queue, item); @@ -136,7 +152,8 @@ bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s) return true; } -bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { exit_main(); return true; diff --git a/ncdc/src/friends.c b/ncdc/src/friends.c index 43f7859..4a7df11 100644 --- a/ncdc/src/friends.c +++ b/ncdc/src/friends.c @@ -2,7 +2,8 @@ #include static bool -ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { size_t i = 0; char c = ' '; @@ -23,7 +24,8 @@ ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av) } static bool -ncdc_cmd_friends_add(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +ncdc_cmd_friends_add(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { char *name = NULL; dc_account_t friend = NULL; @@ -60,7 +62,8 @@ cleanup: } static bool -ncdc_cmd_friends_remove(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +ncdc_cmd_friends_remove(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { char *name = NULL; dc_account_t friend = NULL; @@ -104,7 +107,8 @@ cleanup: } static bool -ncdc_cmd_friends_accept(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +ncdc_cmd_friends_accept(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { char *name = NULL; dc_account_t friend = NULL; @@ -156,7 +160,8 @@ static ncdc_commands_t subcmds[] = { { NULL, NULL } }; -bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { wchar_t *subcmd = NULL; ncdc_commands_t *it = NULL; @@ -167,7 +172,7 @@ bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av) } if (ac <= 1) { - return ncdc_cmd_friends_list(n, ac, av); + return ncdc_cmd_friends_list(n, ac, av, f); } subcmd = av[1]; @@ -180,5 +185,5 @@ bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av) return false; } - return it->handler(n, ac, av); + return it->handler(n, ac, av, f); } diff --git a/ncdc/src/login.c b/ncdc/src/login.c index d61d35c..64000bd 100644 --- a/ncdc/src/login.c +++ b/ncdc/src/login.c @@ -2,7 +2,8 @@ #include #include -bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { char *arg = NULL; bool ret = false; diff --git a/ncdc/src/logout.c b/ncdc/src/logout.c index c3fa440..ffd831c 100644 --- a/ncdc/src/logout.c +++ b/ncdc/src/logout.c @@ -2,7 +2,8 @@ #include #include -bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *f) { bool ret = false; diff --git a/ncdc/src/mainwindow.c b/ncdc/src/mainwindow.c index 9ebc702..afa5f0e 100644 --- a/ncdc/src/mainwindow.c +++ b/ncdc/src/mainwindow.c @@ -110,16 +110,25 @@ ncdc_mainwindow_callback(ncdc_input_t i, wchar_t const *s, size_t len, void *arg) { ncdc_mainwindow_t mainwin = (ncdc_mainwindow_t)arg; + bool ret = false; - if (s != NULL && s[0] == '/') { - if (s[1] == '\0') { - return false; - } - - return ncdc_dispatch(mainwin, s); + if (s == NULL || s[0] == '\0') { + return false; } - return false; + if (s[0] == '/') { + ret = ncdc_dispatch(mainwin, s); + } else { + wchar_t *post = calloc(wcslen(s)+6, sizeof(wchar_t)); + + wcscat(post, L"/post "); + wcscat(post, s); + + ret = ncdc_dispatch(mainwin, post); + free(post); + } + + return ret; } static void ncdc_mainwindow_resize(ncdc_mainwindow_t n) @@ -331,3 +340,13 @@ void ncdc_mainwindow_leftview(ncdc_mainwindow_t n) return_if_true(n == NULL,); n->curview = (n->curview - 1) % n->views->len; } + +dc_channel_t ncdc_mainwindow_current_channel(ncdc_mainwindow_t n) +{ + return_if_true(n == NULL, NULL); + ncdc_textview_t view = g_ptr_array_index(n->views, n->curview); + /* can't post to the log channel, that's for internal use only + */ + return_if_true(view == n->log, NULL); + return ncdc_textview_channel(view); +} diff --git a/ncdc/src/msg.c b/ncdc/src/msg.c index 8f32b04..8a7947b 100644 --- a/ncdc/src/msg.c +++ b/ncdc/src/msg.c @@ -2,13 +2,15 @@ #include #include -bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) +bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, + wchar_t **av, wchar_t const *fullmsg) { return_if_true(ac <= 1, false); char * target = NULL; wchar_t *full_message = NULL; char * message = NULL; + dc_message_t m = NULL; bool ret = false; dc_channel_t c = NULL; ncdc_textview_t v = NULL; @@ -64,15 +66,34 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) ncdc_mainwindow_switchview(n, ncdc_mainwindow_views(n)->len-1); } + if (ac > 2) { + /* also post the rest of the content as a message to the channel + */ + full_message = wcsstr(fullmsg, av[2]); + goto_if_true(full_message == NULL, cleanup); + + message = w_convert(full_message); + goto_if_true(message == NULL, cleanup); + + m = dc_message_new_content(message, -1); + goto_if_true(m == NULL, cleanup); + + ret = dc_api_post_message( + dc_session_api(current_session), + dc_session_me(current_session), + c, m + ); + } + ret = true; cleanup: dc_unref(c); dc_unref(v); + dc_unref(m); free(target); - free(full_message); free(message); return ret; diff --git a/ncdc/src/post.c b/ncdc/src/post.c new file mode 100644 index 0000000..9498979 --- /dev/null +++ b/ncdc/src/post.c @@ -0,0 +1,50 @@ +#include +#include + +bool ncdc_cmd_post(ncdc_mainwindow_t n, size_t ac, wchar_t **av, + wchar_t const *f) +{ + char *str = NULL; + bool ret = false; + dc_message_t m = NULL; + dc_channel_t chan = NULL; + size_t i = 0; + + if (!is_logged_in()) { + return false; + } + + chan = ncdc_mainwindow_current_channel(n); + goto_if_true(chan == NULL, cleanup); + + /* the API only uses multi-byte strings, so convert from wchar_t + */ + str = w_convert(f); + goto_if_true(str == NULL, cleanup); + + /* trim string at least on the left + */ + for (i = 0; isspace(str[i]) && str[i] != '\0'; i++) + ; + goto_if_true (str[i] == '\0', cleanup); + memmove(str, str+i, strlen(str)+1-i); + + m = dc_message_new_content(str, -1); + goto_if_true(m == NULL, cleanup); + + ret = dc_api_post_message( + dc_session_api(current_session), + dc_session_me(current_session), + chan, m + ); + goto_if_true(ret == false, cleanup); + + ret = true; + +cleanup: + + dc_unref(m); + free(str); + + return ret; +} diff --git a/todo.org b/todo.org index 2fd539b..014ed18 100644 --- a/todo.org +++ b/todo.org @@ -1,8 +1,8 @@ -* Channels [0/4] +* Channels [1/4] ** TODO Automatically open window for 1:1 and 1:N ** TODO send v6/api/channels/$ID/ack for "I read that shit" ** TODO add timestamps from messages -** TODO add message sending +** DONE add message sending * Guilds [0/3] ** TODO Guild support code ** TODO Guild view on the left pane