Support tab-complete workspace-specific custom slack emoji (fix #3)

v1
Tony Olagbaiye 7 years ago
parent cb597736f1
commit d9c8e64ffe
No known key found for this signature in database
GPG Key ID: 7420820577A31D11

@ -1,10 +1,9 @@
ifdef DEBUG ifdef DEBUG
CC=clang DBGCFLAGS=-fsanitize=address -fsanitize=leak -fsanitize=undefined
CXX=g++ DBGLDFLAGS=-static-libasan -static-liblsan -static-libubsan
DBGCFLAGS=-fsanitize=address -fsanitize=leak
DBGLDFLAGS=-static-libasan -static-liblsan
endif endif
RM=rm -f RM=rm -f
FIND=find
CFLAGS+=$(DBGCFLAGS) -fno-omit-frame-pointer -fPIC -std=gnu99 -g -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers -Ilibwebsockets/include -Ijson-c CFLAGS+=$(DBGCFLAGS) -fno-omit-frame-pointer -fPIC -std=gnu99 -g -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers -Ilibwebsockets/include -Ijson-c
LDFLAGS+=-shared -g $(DBGCFLAGS) $(DBGLDFLAGS) LDFLAGS+=-shared -g $(DBGCFLAGS) $(DBGLDFLAGS)
LDLIBS=-lgnutls LDLIBS=-lgnutls
@ -40,13 +39,14 @@ SRCS=slack.c \
request/slack-request-chat-postmessage.c \ request/slack-request-chat-postmessage.c \
request/slack-request-channels-list.c \ request/slack-request-channels-list.c \
request/slack-request-conversations-members.c \ request/slack-request-conversations-members.c \
request/slack-request-emoji-list.c \
request/slack-request-users-list.c request/slack-request-users-list.c
OBJS=$(subst .c,.o,$(SRCS)) libwebsockets/lib/libwebsockets.a json-c/libjson-c.a OBJS=$(subst .c,.o,$(SRCS)) libwebsockets/lib/libwebsockets.a json-c/libjson-c.a
all: libwebsockets/lib/libwebsockets.a json-c/libjson-c.a weechat-slack all: libwebsockets/lib/libwebsockets.a json-c/libjson-c.a weechat-slack
weechat-slack: $(OBJS) weechat-slack: $(OBJS)
$(CXX) $(LDFLAGS) -o slack.so $(OBJS) $(LDLIBS) $(CC) $(LDFLAGS) -o slack.so $(OBJS) $(LDLIBS)
ifeq ($(shell which python),) ifeq ($(shell which python),)
slack-emoji.inc: slack-emoji.pl slack-emoji.inc: slack-emoji.pl
@ -70,7 +70,10 @@ depend: .depend
.depend: libwebsockets/lib/libwebsockets.a json-c/libjson-c.a $(SRCS) .depend: libwebsockets/lib/libwebsockets.a json-c/libjson-c.a $(SRCS)
$(RM) ./.depend $(RM) ./.depend
$(CC) $(CFLAGS) -MM $^>>./.depend; $(CC) $(CFLAGS) -MM $^>>./.depend
tidy:
$(FIND) . -name "*.o" -delete
clean: clean:
$(RM) $(OBJS) $(RM) $(OBJS)

@ -77,11 +77,11 @@
- [ ] Implement handling api message =message.message_replied= - [ ] Implement handling api message =message.message_replied=
- [ ] Implement sending websocket =typing= message - [ ] Implement sending websocket =typing= message
** TODO [#B] Implement completion engine (milestone v0.3) ** TODO [#B] Implement completion engine (milestone v0.3)
- [ ] Tab completion for slack emoji (see [[http://github.com/bqv/weechat-slack/issues/3][#3]]) - [X] +Tab completion for slack emoji (see [[http://github.com/bqv/weechat-slack/issues/3][#3]])+
- [X] +Support Slack Emoji+ - [X] +Support Slack Emoji+
- [ ] Support Custom Emoji - [X] +Support Custom Emoji+
- [ ] Tab completion for display/user names (see [[http://github.com/bqv/weechat-slack/issues/1][#1]]) - [ ] Tab completion for display/user names (see [[http://github.com/bqv/weechat-slack/issues/1][#1]])
- [ ] Sort nick-completion by recent (see [[http://github.com/bqv/weechat-slack/issues/4][#4]]) - [ ] Sort nick-completion by recent speakers (see [[http://github.com/bqv/weechat-slack/issues/4][#4]])
** TODO [#B] Implement websocket ping and pong (milestone v0.4) ** TODO [#B] Implement websocket ping and pong (milestone v0.4)
- [ ] Add ping timer and pong handler (see [[http://github.com/bqv/weechat-slack/issues/9][#9]]) - [ ] Add ping timer and pong handler (see [[http://github.com/bqv/weechat-slack/issues/9][#9]])
** TODO [#C] Implement remaining api endpoints and events (milestone v0.5) ** TODO [#C] Implement remaining api endpoints and events (milestone v0.5)
@ -108,7 +108,7 @@
As there is no C library for Slack at the time of As there is no C library for Slack at the time of
writing, this project implements the APIs from writing, this project implements the APIs from
scratch, and as such one could butcher this repository scratch, and as such one could butcher this repository
to create a minimal Slack C library. to create a minimal Slack C library. Up to you.
* License * License

@ -11,6 +11,7 @@
#include "slack-api-hello.h" #include "slack-api-hello.h"
#include "../request/slack-request-channels-list.h" #include "../request/slack-request-channels-list.h"
#include "../request/slack-request-users-list.h" #include "../request/slack-request-users-list.h"
#include "../request/slack-request-emoji-list.h"
int slack_api_hello_handle(struct t_slack_workspace *workspace) int slack_api_hello_handle(struct t_slack_workspace *workspace)
{ {
@ -35,6 +36,12 @@ int slack_api_hello_handle(struct t_slack_workspace *workspace)
if (request) if (request)
slack_workspace_register_request(workspace, request); slack_workspace_register_request(workspace, request);
request = slack_request_emoji_list(workspace,
weechat_config_string(
workspace->options[SLACK_WORKSPACE_OPTION_TOKEN]));
if (request)
slack_workspace_register_request(workspace, request);
return 1; return 1;
} }

@ -0,0 +1,287 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <libwebsockets.h>
#include <json.h>
#include <stdlib.h>
#include <string.h>
#include "../weechat-plugin.h"
#include "../slack.h"
#include "../slack-workspace.h"
#include "../slack-request.h"
#include "../slack-channel.h"
#include "../request/slack-request-emoji-list.h"
static const char *const endpoint = "/api/emoji.list?"
"token=%s";
static inline int json_valid(json_object *object, struct t_slack_workspace *workspace)
{
if (!object)
{
weechat_printf(
workspace->buffer,
_("%s%s: error retrieving emoji: unexpected response from server"),
weechat_prefix("error"), SLACK_PLUGIN_NAME);
//__asm__("int3");
return 0;
}
return 1;
}
static const struct lws_protocols protocols[];
static int callback_http(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct t_slack_request *request = (struct t_slack_request *)user;
struct lws_client_connect_info ccinfo;
int status;
switch (reason)
{
/* because we are protocols[0] ... */
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
weechat_printf(
request->workspace->buffer,
_("%s%s: (%d) error connecting to slack: %s"),
weechat_prefix("error"), SLACK_PLUGIN_NAME, request->idx,
in ? (char *)in : "(null)");
weechat_printf(
request->workspace->buffer,
_("%s%s: (%d) reconnecting..."),
weechat_prefix("error"), SLACK_PLUGIN_NAME, request->idx);
memset(&ccinfo, 0, sizeof(ccinfo)); /* otherwise uninitialized garbage */
ccinfo.context = request->context;
ccinfo.ssl_connection = LCCSCF_USE_SSL;
ccinfo.port = 443;
ccinfo.address = "slack.com";
ccinfo.path = request->uri;
ccinfo.host = ccinfo.address;
ccinfo.origin = ccinfo.address;
ccinfo.method = "GET";
ccinfo.protocol = protocols[0].name;
ccinfo.pwsi = &request->client_wsi;
ccinfo.userdata = request;
lws_client_connect_via_info(&ccinfo);
break;
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
status = lws_http_client_http_response(wsi);
weechat_printf(
request->workspace->buffer,
_("%s%s: (%d) retrieving emoji... (%d)"),
weechat_prefix("network"), SLACK_PLUGIN_NAME, request->idx,
status);
break;
/* chunks of chunked content, with header removed */
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
{
struct t_json_chunk *new_chunk, *last_chunk;
new_chunk = malloc(sizeof(*new_chunk));
new_chunk->data = malloc((1024 * sizeof(char)) + 1);
new_chunk->data[0] = '\0';
new_chunk->next = NULL;
strncat(new_chunk->data, in, (int)len);
if (request->json_chunks)
{
for (last_chunk = request->json_chunks; last_chunk->next;
last_chunk = last_chunk->next);
last_chunk->next = new_chunk;
}
else
{
request->json_chunks = new_chunk;
}
}
return 0; /* don't passthru */
/* uninterpreted http content */
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
{
char buffer[1024 + LWS_PRE];
char *px = buffer + LWS_PRE;
int lenx = sizeof(buffer) - LWS_PRE;
if (lws_http_client_read(wsi, &px, &lenx) < 0)
return -1;
}
return 0; /* don't passthru */
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
{
int chunk_count, i;
char *json_string;
json_object *response, *ok, *error, *emoji;
struct t_json_chunk *chunk_ptr;
chunk_count = 0;
if (request->json_chunks)
{
chunk_count++;
for (chunk_ptr = request->json_chunks; chunk_ptr->next;
chunk_ptr = chunk_ptr->next)
{
chunk_count++;
}
}
json_string = malloc((1024 * sizeof(char) * chunk_count) + 1);
json_string[0] = '\0';
chunk_ptr = request->json_chunks;
for (i = 0; i < chunk_count; i++)
{
strncat(json_string, chunk_ptr->data, 1024);
chunk_ptr = chunk_ptr->next;
free(request->json_chunks->data);
free(request->json_chunks);
request->json_chunks = chunk_ptr;
}
weechat_printf(
request->workspace->buffer,
_("%s%s: (%d) got response: %s"),
weechat_prefix("network"), SLACK_PLUGIN_NAME, request->idx,
json_string);
response = json_tokener_parse(json_string);
ok = json_object_object_get(response, "ok");
if (!json_valid(ok, request->workspace))
{
json_object_put(response);
free(json_string);
return 0;
}
if(json_object_get_boolean(ok))
{
emoji = json_object_object_get(response, "emoji");
if (!json_valid(emoji, request->workspace))
{
json_object_put(response);
free(json_string);
return 0;
}
json_object_object_foreach(emoji, key, val)
{
if (!json_valid(val, request->workspace))
{
continue;
}
slack_workspace_add_emoji(
request->workspace,
key, json_object_get_string(val));
}
}
else
{
error = json_object_object_get(response, "error");
if (!json_valid(error, request->workspace))
{
json_object_put(response);
free(json_string);
return 0;
}
weechat_printf(
request->workspace->buffer,
_("%s%s: (%d) failed to retrieve emoji: %s"),
weechat_prefix("error"), SLACK_PLUGIN_NAME, request->idx,
json_object_get_string(error));
}
json_object_put(response);
free(json_string);
}
/* fallthrough */
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
request->client_wsi = NULL;
/* Does not doing this cause a leak?
lws_cancel_service(lws_get_context(wsi));*/ /* abort poll wait */
break;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static const struct lws_protocols protocols[] = {
{
"http",
callback_http,
0,
0,
},
{ NULL, NULL, 0, 0 }
};
struct t_slack_request *slack_request_emoji_list(
struct t_slack_workspace *workspace,
const char *token)
{
struct t_slack_request *request;
struct lws_context_creation_info ctxinfo;
struct lws_client_connect_info ccinfo;
request = slack_request_alloc(workspace);
size_t urilen = snprintf(NULL, 0, endpoint, token) + 1;
request->uri = malloc(urilen);
snprintf(request->uri, urilen, endpoint, token);
memset(&ctxinfo, 0, sizeof(ctxinfo)); /* otherwise uninitialized garbage */
ctxinfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
ctxinfo.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
ctxinfo.protocols = protocols;
request->context = lws_create_context(&ctxinfo);
if (!request->context)
{
weechat_printf(
workspace->buffer,
_("%s%s: (%d) error connecting to slack: lws init failed"),
weechat_prefix("error"), SLACK_PLUGIN_NAME, request->idx);
return NULL;
}
else
{
weechat_printf(
workspace->buffer,
_("%s%s: (%d) contacting slack.com:443"),
weechat_prefix("network"), SLACK_PLUGIN_NAME, request->idx);
}
memset(&ccinfo, 0, sizeof(ccinfo)); /* otherwise uninitialized garbage */
ccinfo.context = request->context;
ccinfo.ssl_connection = LCCSCF_USE_SSL;
ccinfo.port = 443;
ccinfo.address = "slack.com";
ccinfo.path = request->uri;
ccinfo.host = ccinfo.address;
ccinfo.origin = ccinfo.address;
ccinfo.method = "GET";
ccinfo.protocol = protocols[0].name;
ccinfo.pwsi = &request->client_wsi;
ccinfo.userdata = request;
lws_client_connect_via_info(&ccinfo);
return request;
}

@ -0,0 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _SLACK_REQUEST_EMOJI_LIST_H_
#define _SLACK_REQUEST_EMOJI_LIST_H_
struct t_slack_request *slack_request_emoji_list(
struct t_slack_workspace *workspace,
const char *token);
#endif /*SLACK_REQUEST_EMOJI_LIST_H*/

@ -221,9 +221,6 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason,
json_object_get_string(id), json_object_get_string(id),
json_object_get_string(display_name)); json_object_get_string(display_name));
if (!new_user)
__asm__("int3");
bot_id = json_object_object_get(profile, "bot_id"); bot_id = json_object_object_get(profile, "bot_id");
if (json_valid(bot_id, request->workspace)) if (json_valid(bot_id, request->workspace))
{ {

@ -102,6 +102,7 @@ int slack_emoji_complete_by_name_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer, struct t_gui_buffer *buffer,
struct t_gui_completion *completion) struct t_gui_completion *completion)
{ {
struct t_slack_workspace_emoji *ptr_emoji;
struct t_slack_workspace *workspace; struct t_slack_workspace *workspace;
struct t_slack_channel *channel; struct t_slack_channel *channel;
@ -109,15 +110,25 @@ int slack_emoji_complete_by_name_cb(const void *pointer, void *data,
(void) data; (void) data;
(void) completion_item; (void) completion_item;
workspace = NULL;
slack_buffer_get_workspace_and_channel(buffer, &workspace, &channel); slack_buffer_get_workspace_and_channel(buffer, &workspace, &channel);
size_t i, emoji_count = sizeof(slack_emoji_by_name) size_t i, emoji_count = sizeof(slack_emoji_by_name)
/ sizeof(struct t_slack_emoji_by_name); / sizeof(struct t_slack_emoji_by_name);
if (workspace)
{
for (ptr_emoji = workspace->emoji; ptr_emoji;
ptr_emoji = ptr_emoji->next_emoji)
weechat_hook_completion_list_add(completion,
ptr_emoji->name,
0, WEECHAT_LIST_POS_END);
for (i = 0; i < emoji_count; i++) for (i = 0; i < emoji_count; i++)
weechat_hook_completion_list_add(completion, weechat_hook_completion_list_add(completion,
slack_emoji_by_name[i].name, slack_emoji_by_name[i].name,
0, WEECHAT_LIST_POS_END); 0, WEECHAT_LIST_POS_END);
}
return WEECHAT_RC_OK; return WEECHAT_RC_OK;
} }

@ -388,6 +388,8 @@ struct t_slack_workspace *slack_workspace_alloc(const char *domain)
new_workspace->last_user = NULL; new_workspace->last_user = NULL;
new_workspace->channels = NULL; new_workspace->channels = NULL;
new_workspace->last_channel = NULL; new_workspace->last_channel = NULL;
new_workspace->emoji = NULL;
new_workspace->last_emoji = NULL;
/* create options with null value */ /* create options with null value */
for (i = 0; i < SLACK_WORKSPACE_NUM_OPTIONS; i++) for (i = 0; i < SLACK_WORKSPACE_NUM_OPTIONS; i++)
@ -911,3 +913,60 @@ void slack_workspace_register_request(struct t_slack_workspace *workspace,
workspace->requests = request; workspace->requests = request;
workspace->last_request = request; workspace->last_request = request;
} }
struct t_slack_workspace_emoji *slack_workspace_emoji_search(
struct t_slack_workspace *workspace,
const char *name)
{
struct t_slack_workspace_emoji *ptr_emoji;
if (!workspace || !name)
return NULL;
for (ptr_emoji = workspace->emoji; ptr_emoji;
ptr_emoji = ptr_emoji->next_emoji)
{
if (weechat_strcasecmp(ptr_emoji->name, name) == 0)
return ptr_emoji;
}
return NULL;
}
struct t_slack_workspace_emoji *slack_workspace_add_emoji(
struct t_slack_workspace *workspace,
const char *name, const char *url)
{
struct t_slack_workspace_emoji *ptr_emoji, *new_emoji;
char shortname[SLACK_WORKSPACE_EMOJI_SHORTNAME_MAX_LEN + 1];
(void) url;
if (!workspace || !name || !name[0])
return NULL;
snprintf(shortname, SLACK_WORKSPACE_EMOJI_SHORTNAME_MAX_LEN + 1,
":%s:", name);
ptr_emoji = slack_workspace_emoji_search(workspace, shortname);
if (ptr_emoji)
{
return ptr_emoji;
}
if ((new_emoji = malloc(sizeof(*new_emoji))) == NULL)
return NULL;
new_emoji->name = strdup(shortname);
new_emoji->url = strdup(url);
new_emoji->prev_emoji = workspace->last_emoji;
new_emoji->next_emoji = NULL;
if (workspace->last_emoji)
(workspace->last_emoji)->next_emoji = new_emoji;
else
workspace->emoji = new_emoji;
workspace->last_emoji = new_emoji;
return new_emoji;
}

@ -5,9 +5,20 @@
#ifndef _SLACK_WORKSPACE_H_ #ifndef _SLACK_WORKSPACE_H_
#define _SLACK_WORKSPACE_H_ #define _SLACK_WORKSPACE_H_
#define SLACK_WORKSPACE_EMOJI_SHORTNAME_MAX_LEN 1 + 100 + 1
extern struct t_slack_workspace *slack_workspaces; extern struct t_slack_workspace *slack_workspaces;
extern struct t_slack_workspace *last_slack_workspace; extern struct t_slack_workspace *last_slack_workspace;
struct t_slack_workspace_emoji
{
char *name;
char *url;
struct t_slack_workspace_emoji *prev_emoji;
struct t_slack_workspace_emoji *next_emoji;
};
enum t_slack_workspace_option enum t_slack_workspace_option
{ {
SLACK_WORKSPACE_OPTION_TOKEN, SLACK_WORKSPACE_OPTION_TOKEN,
@ -51,6 +62,8 @@ struct t_slack_workspace
struct t_slack_user *last_user; struct t_slack_user *last_user;
struct t_slack_channel *channels; struct t_slack_channel *channels;
struct t_slack_channel *last_channel; struct t_slack_channel *last_channel;
struct t_slack_workspace_emoji *emoji;
struct t_slack_workspace_emoji *last_emoji;
struct t_slack_workspace *prev_workspace; struct t_slack_workspace *prev_workspace;
struct t_slack_workspace *next_workspace; struct t_slack_workspace *next_workspace;
}; };
@ -72,5 +85,11 @@ int slack_workspace_connect(struct t_slack_workspace *workspace);
int slack_workspace_timer_cb(const void *pointer, void *data, int remaining_calls); int slack_workspace_timer_cb(const void *pointer, void *data, int remaining_calls);
void slack_workspace_register_request(struct t_slack_workspace *workspace, void slack_workspace_register_request(struct t_slack_workspace *workspace,
struct t_slack_request *request); struct t_slack_request *request);
struct t_slack_workspace_emoji *slack_workspace_emoji_search(
struct t_slack_workspace *workspace,
const char *name);
struct t_slack_workspace_emoji *slack_workspace_add_emoji(
struct t_slack_workspace *workspace,
const char *name, const char *url);
#endif /*SLACK_WORKSPACE_H*/ #endif /*SLACK_WORKSPACE_H*/

Loading…
Cancel
Save