degesch: halfplement /msg, /query, /notice
Which involved some refactoring.
This commit is contained in:
parent
2633eda69a
commit
c088f081d7
314
degesch.c
314
degesch.c
|
@ -2381,6 +2381,155 @@ irc_process_message (const struct irc_message *msg,
|
|||
irc_process_numeric (ctx, msg, numeric);
|
||||
}
|
||||
|
||||
// --- Message autosplitting magic ---------------------------------------------
|
||||
|
||||
static bool
|
||||
wrap_text (const char *message,
|
||||
int line_max, struct str_vector *output, struct error **e)
|
||||
{
|
||||
// Attempt to split the message if it doesn't completely fit into a single
|
||||
// IRC protocol message while trying not to break UTF-8. Unicode can still
|
||||
// end up being wrong, though. As well as any mIRC formatting.
|
||||
//
|
||||
// TODO: at least try to word-wrap if nothing else
|
||||
|
||||
for (int message_left = strlen (message); message_left; )
|
||||
{
|
||||
struct str m;
|
||||
str_init (&m);
|
||||
|
||||
int part_left = MIN (line_max, message_left);
|
||||
bool empty = true;
|
||||
while (true)
|
||||
{
|
||||
const char *next = utf8_next (message, message_left);
|
||||
hard_assert (next);
|
||||
|
||||
int char_len = message - next;
|
||||
if (char_len > part_left)
|
||||
break;
|
||||
|
||||
str_append_data (&m, message, char_len);
|
||||
|
||||
message += char_len;
|
||||
message_left -= char_len;
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (!empty)
|
||||
str_vector_add (output, m.str);
|
||||
|
||||
str_free (&m);
|
||||
|
||||
if (empty)
|
||||
{
|
||||
// Well, that's just weird
|
||||
error_set (e,
|
||||
"Message splitting was unsuccessful as there was "
|
||||
"too little room for UTF-8 characters");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/// Automatically splits messages that arrive at other clients with our prefix
|
||||
/// so that they don't arrive cut off by the server
|
||||
static bool
|
||||
irc_autosplit_message (struct app_context *ctx, const char *message,
|
||||
int fixed_part, struct str_vector *output, struct error **e)
|
||||
{
|
||||
// :<nick>!<user>@<host> <fixed-part><message>
|
||||
int space_in_one_message = 0;
|
||||
if (ctx->irc_user_host)
|
||||
space_in_one_message = 510
|
||||
- 1 - (int) strlen (ctx->irc_user->nickname)
|
||||
- 1 - (int) strlen (ctx->irc_user_host)
|
||||
- 1 - fixed_part;
|
||||
|
||||
// However we don't always have the full info for message splitting
|
||||
if (!space_in_one_message)
|
||||
str_vector_add (output, message);
|
||||
else if (!wrap_text (message, space_in_one_message, output, e))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct send_autosplit_args;
|
||||
|
||||
typedef void (*send_autosplit_logger_fn) (struct app_context *ctx,
|
||||
struct send_autosplit_args *args, struct buffer *buffer, const char *line);
|
||||
|
||||
struct send_autosplit_args
|
||||
{
|
||||
const char *command; ///< E.g. PRIVMSG or NOTICE
|
||||
const char *target; ///< User or channel
|
||||
const char *message; ///< A message to be autosplit
|
||||
send_autosplit_logger_fn logger; ///< Logger for all resulting lines
|
||||
};
|
||||
|
||||
static void
|
||||
send_autosplit_message (struct app_context *ctx, struct send_autosplit_args a)
|
||||
{
|
||||
struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, a.target);
|
||||
int fixed_part = strlen (a.command) + 1 + strlen (a.target) + 1 + 1;
|
||||
|
||||
struct str_vector lines;
|
||||
str_vector_init (&lines);
|
||||
struct error *e = NULL;
|
||||
if (!irc_autosplit_message (ctx, a.message, fixed_part, &lines, &e))
|
||||
{
|
||||
buffer_send_error (ctx,
|
||||
buffer ? buffer : ctx->server_buffer, "%s", e->message);
|
||||
error_free (e);
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < lines.len; i++)
|
||||
{
|
||||
irc_send (ctx, "%s %s :%s", a.command, a.target, lines.vector[i]);
|
||||
a.logger (ctx, &a, buffer, lines.vector[i]);
|
||||
}
|
||||
end:
|
||||
str_vector_free (&lines);
|
||||
}
|
||||
|
||||
static void
|
||||
log_outcoming_privmsg (struct app_context *ctx,
|
||||
struct send_autosplit_args *a, struct buffer *buffer, const char *line)
|
||||
{
|
||||
if (buffer)
|
||||
buffer_send (ctx, buffer, BUFFER_LINE_PRIVMSG, 0,
|
||||
ctx->irc_user->nickname, NULL, "%s", line);
|
||||
else
|
||||
// TODO: fix logging
|
||||
buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0,
|
||||
NULL, NULL, "MSG(%s): %s", a->target, line);
|
||||
}
|
||||
|
||||
#define SEND_AUTOSPLIT_PRIVMSG(ctx, target, message) \
|
||||
send_autosplit_message ((ctx), (struct send_autosplit_args) \
|
||||
{ "PRIVMSG", (target), (message), log_outcoming_privmsg })
|
||||
|
||||
static void
|
||||
log_outcoming_notice (struct app_context *ctx,
|
||||
struct send_autosplit_args *a, struct buffer *buffer, const char *line)
|
||||
{
|
||||
if (buffer)
|
||||
buffer_send (ctx, buffer, BUFFER_LINE_NOTICE, 0,
|
||||
ctx->irc_user->nickname, NULL, "%s", line);
|
||||
else
|
||||
// TODO: fix logging
|
||||
buffer_send (ctx, ctx->server_buffer, BUFFER_LINE_STATUS, 0,
|
||||
NULL, NULL, "Notice -> %s: %s", a->target, line);
|
||||
}
|
||||
|
||||
#define SEND_AUTOSPLIT_NOTICE(ctx, target, message) \
|
||||
send_autosplit_message ((ctx), (struct send_autosplit_args) \
|
||||
{ "NOTICE", (target), (message), log_outcoming_notice })
|
||||
|
||||
// --- User input handling -----------------------------------------------------
|
||||
|
||||
static void handle_command_help (struct app_context *, char *);
|
||||
|
@ -2490,6 +2639,108 @@ handle_command_buffer (struct app_context *ctx, char *arguments)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_command_msg (struct app_context *ctx, char *arguments)
|
||||
{
|
||||
if (ctx->current_buffer->type == BUFFER_GLOBAL)
|
||||
{
|
||||
buffer_send_error (ctx, ctx->current_buffer,
|
||||
"Can't send messages from a global buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->irc_fd == -1)
|
||||
{
|
||||
buffer_send_error (ctx, ctx->server_buffer, "Not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*arguments)
|
||||
{
|
||||
// TODO: show usage or something
|
||||
return;
|
||||
}
|
||||
|
||||
char *target = cut_word (&arguments);
|
||||
if (!*arguments)
|
||||
buffer_send_error (ctx, ctx->server_buffer, "No text to send");
|
||||
else
|
||||
SEND_AUTOSPLIT_PRIVMSG (ctx, target, arguments);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_command_query (struct app_context *ctx, char *arguments)
|
||||
{
|
||||
if (ctx->current_buffer->type == BUFFER_GLOBAL)
|
||||
{
|
||||
buffer_send_error (ctx, ctx->current_buffer,
|
||||
"Can't send messages from a global buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->irc_fd == -1)
|
||||
{
|
||||
buffer_send_error (ctx, ctx->server_buffer, "Not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*arguments)
|
||||
{
|
||||
// TODO: show usage or something
|
||||
return;
|
||||
}
|
||||
|
||||
char *target = cut_word (&arguments);
|
||||
if (irc_is_channel (ctx, target))
|
||||
{
|
||||
buffer_send_error (ctx, ctx->server_buffer, "Cannot query a channel");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*arguments)
|
||||
buffer_send_error (ctx, ctx->server_buffer, "No text to send");
|
||||
else
|
||||
{
|
||||
struct buffer *buffer = str_map_find (&ctx->irc_buffer_map, target);
|
||||
if (!buffer)
|
||||
{
|
||||
// TODO: create a buffer for this user
|
||||
// TODO: see irc_handle_privmsg
|
||||
}
|
||||
buffer_activate (ctx, buffer);
|
||||
SEND_AUTOSPLIT_PRIVMSG (ctx, target, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_command_notice (struct app_context *ctx, char *arguments)
|
||||
{
|
||||
if (ctx->current_buffer->type == BUFFER_GLOBAL)
|
||||
{
|
||||
buffer_send_error (ctx, ctx->current_buffer,
|
||||
"Can't send messages from a global buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->irc_fd == -1)
|
||||
{
|
||||
buffer_send_error (ctx, ctx->server_buffer, "Not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*arguments)
|
||||
{
|
||||
// TODO: show usage or something
|
||||
return;
|
||||
}
|
||||
|
||||
char *target = cut_word (&arguments);
|
||||
if (!*arguments)
|
||||
buffer_send_error (ctx, ctx->server_buffer, "No text to send");
|
||||
else
|
||||
SEND_AUTOSPLIT_NOTICE (ctx, target, arguments);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_command_quit (struct app_context *ctx, char *arguments)
|
||||
{
|
||||
|
@ -2599,10 +2850,14 @@ g_command_handlers[] =
|
|||
"[message]" },
|
||||
{ "buffer", handle_command_buffer, "Manage buffers",
|
||||
"list | clear | move | { close [<number> | <name>] } | <number>" },
|
||||
|
||||
{ "msg", handle_command_msg, "Send message to a nick or channel",
|
||||
"<target> <message>" },
|
||||
{ "query", handle_command_query, "Send a private message to a nick",
|
||||
"<nick> <message>" },
|
||||
{ "notice", handle_command_notice, "Send notice to a nick or channel",
|
||||
"<target> <message>" },
|
||||
#if 0
|
||||
{ "msg", NULL, "", "" },
|
||||
{ "query", NULL, "", "" },
|
||||
{ "notice", NULL, "", "" },
|
||||
{ "ctcp", NULL, "", "" },
|
||||
{ "me", NULL, "", "" },
|
||||
#endif
|
||||
|
@ -2722,58 +2977,7 @@ send_message_to_target (struct app_context *ctx,
|
|||
return;
|
||||
}
|
||||
|
||||
// We don't always have the full info for message splitting
|
||||
int space_in_one_message = 0;
|
||||
if (ctx->irc_user_host)
|
||||
// :<nick>!<user>@<host> PRIVMSG <target> :<message>
|
||||
space_in_one_message = 510
|
||||
- 1 - (int) strlen (ctx->irc_user->nickname)
|
||||
- 1 - (int) strlen (ctx->irc_user_host)
|
||||
- 1 - 7 - 1 - strlen (target) - 1 - 1;
|
||||
|
||||
// Attempt to split the message if it doesn't completely fit into
|
||||
// a single IRC protocol message while trying not to break UTF-8.
|
||||
// Unicode can still end up being wrong, though.
|
||||
// TODO: at least try to word-wrap if nothing else
|
||||
for (int message_left = strlen (message); message_left; )
|
||||
{
|
||||
struct str m;
|
||||
str_init (&m);
|
||||
|
||||
int part_left = MIN (space_in_one_message, message_left);
|
||||
if (!space_in_one_message)
|
||||
part_left = message_left;
|
||||
|
||||
bool empty = true;
|
||||
while (true)
|
||||
{
|
||||
const char *next = utf8_next (message, message_left);
|
||||
hard_assert (next);
|
||||
|
||||
int char_len = message - next;
|
||||
if (char_len > part_left)
|
||||
break;
|
||||
|
||||
str_append_data (&m, message, char_len);
|
||||
|
||||
message += char_len;
|
||||
message_left -= char_len;
|
||||
empty = false;
|
||||
}
|
||||
if (empty)
|
||||
{
|
||||
// Well, that's just weird
|
||||
buffer_send_error (ctx, buffer, "%s",
|
||||
"Message splitting was unsuccessful as there was "
|
||||
"too little room for UTF-8 characters");
|
||||
message_left = 0;
|
||||
}
|
||||
|
||||
irc_send (ctx, "PRIVMSG %s :%s", target, m.str);
|
||||
buffer_send (ctx, buffer, BUFFER_LINE_PRIVMSG, 0,
|
||||
ctx->irc_user->nickname, NULL, "%s", m.str);
|
||||
str_free (&m);
|
||||
}
|
||||
SEND_AUTOSPLIT_PRIVMSG (ctx, target, message);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
Loading…
Reference in New Issue