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);
|
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 -----------------------------------------------------
|
// --- User input handling -----------------------------------------------------
|
||||||
|
|
||||||
static void handle_command_help (struct app_context *, char *);
|
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
|
static void
|
||||||
handle_command_quit (struct app_context *ctx, char *arguments)
|
handle_command_quit (struct app_context *ctx, char *arguments)
|
||||||
{
|
{
|
||||||
|
@ -2599,10 +2850,14 @@ g_command_handlers[] =
|
||||||
"[message]" },
|
"[message]" },
|
||||||
{ "buffer", handle_command_buffer, "Manage buffers",
|
{ "buffer", handle_command_buffer, "Manage buffers",
|
||||||
"list | clear | move | { close [<number> | <name>] } | <number>" },
|
"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
|
#if 0
|
||||||
{ "msg", NULL, "", "" },
|
|
||||||
{ "query", NULL, "", "" },
|
|
||||||
{ "notice", NULL, "", "" },
|
|
||||||
{ "ctcp", NULL, "", "" },
|
{ "ctcp", NULL, "", "" },
|
||||||
{ "me", NULL, "", "" },
|
{ "me", NULL, "", "" },
|
||||||
#endif
|
#endif
|
||||||
|
@ -2722,58 +2977,7 @@ send_message_to_target (struct app_context *ctx,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't always have the full info for message splitting
|
SEND_AUTOSPLIT_PRIVMSG (ctx, target, message);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
Loading…
Reference in New Issue