Added a chat history plugin.
The mod_chat_history plugin provides chat history for all public chat messages. Can be configured in the following ways: - history_max: max number of messages stored in history - history_default: the default number of messages to be returned when invoking !history - history_connect: if > 0, then this number of messages is automatically sent when connecting to the hub Removed the built-in !history command in favour of the mod_chat_history plug-in. Make sure we unescape all chat messages before forwarding any of them to plugins. Update example plugins.conf in documentation directory.
This commit is contained in:
parent
a9ed03cf38
commit
875f55a401
|
@ -1,16 +1,63 @@
|
|||
# ATTENTION!
|
||||
# Plugins are invoked in the order of listing in the plugin config file.
|
||||
|
||||
# auth user
|
||||
# file={path for DB file with user auth information}
|
||||
|
||||
|
||||
# Sqlite based user authentication.
|
||||
#
|
||||
# This plugin provides a Sqlite based authentication database for
|
||||
# registered users.
|
||||
# Use the uhub-passwd utility to create the database and add/remove users.
|
||||
#
|
||||
# Parameters:
|
||||
# file: path/filename for database.
|
||||
#
|
||||
plugin /var/lib/uhub/mod_auth_sqlite.so "file=/etc/uhub/users.db"
|
||||
|
||||
# log subsystem.
|
||||
# file={/path/to/logfile}
|
||||
|
||||
# Log file writer
|
||||
#
|
||||
# Parameters:
|
||||
# file: path/filename for log file.
|
||||
# syslog: if true then syslog is used instead of writing to a file (Unix only)
|
||||
plugin /var/lib/uhub/mod_logging.so "file=/var/log/uhub.log"
|
||||
|
||||
#
|
||||
# plugin /var/lib/uhub/mod_auth_simple.so
|
||||
|
||||
#
|
||||
# A simple example plugin
|
||||
# plugin /var/lib/uhub/mod_example.so
|
||||
|
||||
# Load the chat history plugin.
|
||||
#
|
||||
# This plugin provides chat history when users are connecting, or
|
||||
# when users invoke the !history command.
|
||||
# The history command can optionally take a parameter to indicate how many lines of history is requested.
|
||||
#
|
||||
# Parameters:
|
||||
# history_max: the maximum number of messages to keep in history
|
||||
# history_default: when !history is provided without arguments, then this default number of messages are returned.
|
||||
# history_connect: the number of chat history messages to send when users connect (0 = do not send any history)
|
||||
plugin /var/lib/uhub/mod_chat_history.so "history_max=200 history_default=10 history_connect=5"
|
||||
|
||||
# A plugin sending a welcome message.
|
||||
#
|
||||
# This plugin provides the following commands:
|
||||
# !motd - Message of the day
|
||||
# !rules - Show hub rules.
|
||||
#
|
||||
# Parameters:
|
||||
# motd: path/filename for the welcome message (message of the day)
|
||||
# rules: path/filenam for the rules file
|
||||
#
|
||||
# NOTE: The files MUST exist, however if you do not wish to provide one then these parameters can be omitted.
|
||||
#
|
||||
# The motd/rules files can do the following substitutions:
|
||||
# %n - Nickname of the user who entered the hub or issued the command.
|
||||
# %a - IP address of the user
|
||||
# %% - Becomes '%'
|
||||
# %H - Hour 24-hour format (00-23) (Hub local time)
|
||||
# %I - Hour 12-hour format (01-12) (Hub local time)
|
||||
# %P - 'AM' or 'PM'
|
||||
# %p - 'am' or 'pm'
|
||||
# %M - Minutes (00-59) (Hub local time)
|
||||
# %S - Seconds (00-60) (Hub local time)
|
||||
plugin /var/lib/uhub/mod_welcome.so "motd=/etc/uhub/motd.txt rules=/etc/uhub/rules.txt"
|
||||
|
||||
|
||||
|
|
|
@ -685,49 +685,6 @@ static int command_broadcast(struct command_base* cbase, struct hub_user* user,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int command_history(struct command_base* cbase, struct hub_user* user, struct hub_command* cmd)
|
||||
{
|
||||
struct cbuffer* buf;
|
||||
struct linked_list* messages = cbase->hub->chat_history;
|
||||
char* message;
|
||||
char* maxlines_str = list_get_first(cmd->args);
|
||||
int maxlines = 0;
|
||||
int skiplines = 0;
|
||||
int total = list_size(messages);
|
||||
|
||||
if (total == 0)
|
||||
return command_status(cbase, user, cmd, cbuf_create_const("No messages."));
|
||||
|
||||
buf = cbuf_create(MAX_HELP_MSG);
|
||||
if (maxlines_str)
|
||||
maxlines = uhub_atoi(maxlines_str);
|
||||
|
||||
if (maxlines <= 0 || maxlines > total)
|
||||
maxlines = total;
|
||||
|
||||
if (maxlines != total)
|
||||
{
|
||||
skiplines = total - maxlines;
|
||||
cbuf_append_format(buf, "*** %s: Displaying %d of %d message%s:", cmd->prefix, maxlines, total, ((total != 1) ? "s" : ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
cbuf_append_format(buf, "*** %s: Displaying %d message%s:", cmd->prefix, total, ((total != 1) ? "s" : ""));
|
||||
}
|
||||
cbuf_append(buf, "\n");
|
||||
|
||||
message = (char*) list_get_first(messages);
|
||||
while (message)
|
||||
{
|
||||
if (--skiplines < 0)
|
||||
cbuf_append(buf, message);
|
||||
message = (char*) list_get_next(messages);
|
||||
}
|
||||
cbuf_append(buf, "\n");
|
||||
send_message(cbase, user, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int command_log(struct command_base* cbase, struct hub_user* user, struct hub_command* cmd)
|
||||
{
|
||||
struct cbuffer* buf;
|
||||
|
@ -948,7 +905,6 @@ void commands_builtin_add(struct command_base* cbase)
|
|||
#endif
|
||||
ADD_COMMAND("getip", 5, "n", auth_cred_operator, command_getip, "Show IP address for a user" );
|
||||
ADD_COMMAND("help", 4, "?c",auth_cred_guest, command_help, "Show this help message." );
|
||||
ADD_COMMAND("history", 7, "?N",auth_cred_guest, command_history, "Show the last chat messages." );
|
||||
ADD_COMMAND("kick", 4, "n", auth_cred_operator, command_kick, "Kick a user" );
|
||||
ADD_COMMAND("log", 3, "", auth_cred_operator, command_log, "Display log" );
|
||||
ADD_COMMAND("mute", 4, "n", auth_cred_operator, command_mute, "Mute user" );
|
||||
|
|
|
@ -54,10 +54,10 @@ enum command_parse_status
|
|||
*/
|
||||
struct hub_command
|
||||
{
|
||||
enum command_parse_status status; /**<<< "Status of the hub_command." */
|
||||
const char* message; /**<<< "The complete message." */
|
||||
char* prefix; /**<<< "The prefix extracted from the message." */
|
||||
struct linked_list* args; /**<<< "List of all parsed arguments from the message. Type depends on expectations." */
|
||||
enum command_parse_status status; /**<<< "Status of the hub_command." */
|
||||
command_handler handler; /**<<< "The function handler to call in order to invoke this command." */
|
||||
const struct hub_user* user; /**<<< "The user who invoked this command." */
|
||||
void* ptr; /**<<< "A pointer of data which came from struct command_handler" */
|
||||
|
|
|
@ -265,6 +265,13 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
|
|||
if (!message)
|
||||
return 0;
|
||||
|
||||
message_decoded = adc_msg_unescape(message);
|
||||
if (!message_decoded)
|
||||
{
|
||||
hub_free(message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!user_is_logged_in(u))
|
||||
{
|
||||
hub_free(message);
|
||||
|
@ -290,9 +297,7 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
|
|||
}
|
||||
else
|
||||
{
|
||||
message_decoded = adc_msg_unescape(message);
|
||||
relay = command_invoke(hub->commands, u, message_decoded);
|
||||
hub_free(message_decoded);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,13 +312,13 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
|
|||
plugin_st status = st_default;
|
||||
if (broadcast)
|
||||
{
|
||||
status = plugin_handle_chat_message(hub, u, message, 0);
|
||||
status = plugin_handle_chat_message(hub, u, message_decoded, 0);
|
||||
}
|
||||
else if (private_msg)
|
||||
{
|
||||
struct hub_user* target = uman_get_user_by_sid(hub, cmd->target);
|
||||
if (target)
|
||||
status = plugin_handle_private_message(hub, u, target, message, 0);
|
||||
status = plugin_handle_private_message(hub, u, target, message_decoded, 0);
|
||||
else
|
||||
relay = 0;
|
||||
}
|
||||
|
@ -327,39 +332,15 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
|
|||
/* adc_msg_remove_named_argument(cmd, "PM"); */
|
||||
if (broadcast)
|
||||
{
|
||||
hub_chat_history_add(hub, u, cmd);
|
||||
plugin_log_chat_message(hub, u, message, 0);
|
||||
plugin_log_chat_message(hub, u, message_decoded, 0);
|
||||
}
|
||||
ret = route_message(hub, u, cmd);
|
||||
}
|
||||
hub_free(message);
|
||||
hub_free(message_decoded);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void hub_chat_history_add(struct hub_info* hub, struct hub_user* user, struct adc_message* cmd)
|
||||
{
|
||||
char* msg_esc = adc_msg_get_argument(cmd, 0);
|
||||
char* message = adc_msg_unescape(msg_esc);
|
||||
size_t loglen = strlen(message) + strlen(user->id.nick) + 13;
|
||||
char* log = hub_malloc(loglen + 1);
|
||||
snprintf(log, loglen, "%s <%s> %s\n", get_timestamp(time(NULL)), user->id.nick, message);
|
||||
log[loglen] = '\0';
|
||||
list_append(hub->chat_history, log);
|
||||
while (list_size(hub->chat_history) > (size_t) hub->config->max_chat_history)
|
||||
{
|
||||
char* msg = list_get_first(hub->chat_history);
|
||||
list_remove(hub->chat_history, msg);
|
||||
hub_free(msg);
|
||||
}
|
||||
hub_free(message);
|
||||
hub_free(msg_esc);
|
||||
}
|
||||
|
||||
void hub_chat_history_clear(struct hub_info* hub)
|
||||
{
|
||||
list_clear(hub->chat_history, &hub_free);
|
||||
}
|
||||
|
||||
void hub_send_support(struct hub_info* hub, struct hub_user* u)
|
||||
{
|
||||
if (user_is_connecting(u) || user_is_logged_in(u))
|
||||
|
@ -803,20 +784,7 @@ struct hub_info* hub_start_service(struct hub_config* config)
|
|||
return 0;
|
||||
}
|
||||
|
||||
hub->chat_history = (struct linked_list*) list_create();
|
||||
hub->logout_info = (struct linked_list*) list_create();
|
||||
if (!hub->chat_history)
|
||||
{
|
||||
net_con_close(hub->server);
|
||||
list_destroy(hub->chat_history);
|
||||
list_destroy(hub->logout_info);
|
||||
hub_free(hub->recvbuf);
|
||||
hub_free(hub->sendbuf);
|
||||
uman_shutdown(hub);
|
||||
hub_free(hub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
server_alt_port_start(hub, config);
|
||||
|
||||
hub->status = hub_status_running;
|
||||
|
@ -844,8 +812,6 @@ void hub_shutdown_service(struct hub_info* hub)
|
|||
hub->status = hub_status_stopped;
|
||||
hub_free(hub->sendbuf);
|
||||
hub_free(hub->recvbuf);
|
||||
hub_chat_history_clear(hub);
|
||||
list_destroy(hub->chat_history);
|
||||
list_clear(hub->logout_info, &hub_free);
|
||||
list_destroy(hub->logout_info);
|
||||
command_shutdown(hub->commands);
|
||||
|
|
|
@ -109,7 +109,6 @@ struct hub_info
|
|||
char* recvbuf; /* Global receive buffer */
|
||||
char* sendbuf; /* Global send buffer */
|
||||
|
||||
struct linked_list* chat_history; /* Chat history */
|
||||
struct linked_list* logout_info; /* Log of people logging out. */
|
||||
|
||||
struct command_base* commands; /* Hub command handler */
|
||||
|
@ -151,16 +150,6 @@ extern int hub_handle_password(struct hub_info* hub, struct hub_user* u, struct
|
|||
*/
|
||||
extern int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc_message* cmd);
|
||||
|
||||
/**
|
||||
* Add a chat message to the chat history
|
||||
*/
|
||||
extern void hub_chat_history_add(struct hub_info* hub, struct hub_user* user, struct adc_message* cmd);
|
||||
|
||||
/**
|
||||
* Clear the chat history.
|
||||
*/
|
||||
extern void hub_chat_history_clear(struct hub_info* hub);
|
||||
|
||||
/**
|
||||
* Used internally by hub_handle_info
|
||||
* @return 1 if nickname is OK, or 0 if nickname is not accepted.
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* uhub - A tiny ADC p2p connection hub
|
||||
* Copyright (C) 2007-2012, Jan Vidar Krey
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "plugin_api/handle.h"
|
||||
#include "plugin_api/command_api.h"
|
||||
#include "util/config_token.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/misc.h"
|
||||
#include "util/list.h"
|
||||
#include "util/cbuffer.h"
|
||||
|
||||
#define MAX_HISTORY_SIZE 16384
|
||||
|
||||
struct chat_history_data
|
||||
{
|
||||
size_t history_max; ///<<< "the maximum number of chat messages kept in history."
|
||||
size_t history_default; ///<<< "the default number of chat messages returned if no limit was provided"
|
||||
size_t history_connect; ///<<< "the number of chat messages provided when users connect to the hub."
|
||||
struct linked_list* chat_history; ///<<< "The chat history storage."
|
||||
struct plugin_command_handle* command_history_handle; ///<<< "A handle to the !history command."
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a chat message to history.
|
||||
*/
|
||||
static void history_add(struct plugin_handle* plugin, struct plugin_user* from, const char* message, int flags)
|
||||
{
|
||||
size_t loglen = strlen(message) + strlen(from->nick) + 13;
|
||||
struct chat_history_data* data = (struct chat_history_data*) plugin->ptr;
|
||||
char* log = hub_malloc(loglen + 1);
|
||||
|
||||
snprintf(log, loglen, "%s <%s> %s\n", get_timestamp(time(NULL)), from->nick, message);
|
||||
log[loglen] = '\0';
|
||||
list_append(data->chat_history, log);
|
||||
while (list_size(data->chat_history) > data->history_max)
|
||||
{
|
||||
char* msg = list_get_first(data->chat_history);
|
||||
list_remove(data->chat_history, msg);
|
||||
hub_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain 'num' messages from the chat history and append them to outbuf.
|
||||
*
|
||||
* @return the number of messages added to the buffer.
|
||||
*/
|
||||
static size_t get_messages(struct chat_history_data* data, size_t num, struct cbuffer* outbuf)
|
||||
{
|
||||
struct linked_list* messages = data->chat_history;
|
||||
char* message;
|
||||
int skiplines = 0;
|
||||
size_t lines = 0;
|
||||
int total = list_size(messages);
|
||||
|
||||
if (total == 0)
|
||||
return 0;
|
||||
|
||||
if (num <= 0 || num > total)
|
||||
num = total;
|
||||
|
||||
if (num != total)
|
||||
skiplines = total - num;
|
||||
|
||||
cbuf_append(outbuf, "\n");
|
||||
message = (char*) list_get_first(messages);
|
||||
while (message)
|
||||
{
|
||||
if (--skiplines < 0)
|
||||
{
|
||||
cbuf_append(outbuf, message);
|
||||
lines++;
|
||||
}
|
||||
message = (char*) list_get_next(messages);
|
||||
}
|
||||
cbuf_append(outbuf, "\n");
|
||||
return lines;
|
||||
}
|
||||
|
||||
void user_login(struct plugin_handle* plugin, struct plugin_user* user)
|
||||
{
|
||||
struct chat_history_data* data = (struct chat_history_data*) plugin->ptr;
|
||||
struct cbuffer* buf = NULL;
|
||||
// size_t messages = 0;
|
||||
|
||||
if (data->history_connect > 0 && list_size(data->chat_history) > 0)
|
||||
{
|
||||
buf = cbuf_create(MAX_HISTORY_SIZE);
|
||||
cbuf_append(buf, "Chat history:\n");
|
||||
get_messages(data, data->history_connect, buf);
|
||||
plugin->hub.send_message(plugin, user, cbuf_get(buf));
|
||||
cbuf_destroy(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a status message back to the user who issued the !history command.
|
||||
*/
|
||||
static int command_status(struct plugin_handle* plugin, struct plugin_user* user, struct plugin_command* cmd, struct cbuffer* buf)
|
||||
{
|
||||
struct cbuffer* msg = cbuf_create(cbuf_size(buf) + strlen(cmd->prefix) + 8);
|
||||
cbuf_append_format(msg, "*** %s: %s", cmd->prefix, cbuf_get(buf));
|
||||
plugin->hub.send_message(plugin, user, cbuf_get(msg));
|
||||
cbuf_destroy(msg);
|
||||
cbuf_destroy(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback function for handling the !history command.
|
||||
*/
|
||||
static int command_history(struct plugin_handle* plugin, struct plugin_user* user, struct plugin_command* cmd)
|
||||
{
|
||||
struct cbuffer* buf;
|
||||
struct chat_history_data* data = (struct chat_history_data*) plugin->ptr;
|
||||
int maxlines = 0;
|
||||
|
||||
if (!list_size(data->chat_history))
|
||||
return command_status(plugin, user, cmd, cbuf_create_const("No messages."));
|
||||
|
||||
if (list_size(cmd->args) > 0)
|
||||
maxlines = (int) (intptr_t) ((intptr_t*) (void*) list_get_first(cmd->args));
|
||||
else
|
||||
maxlines = data->history_default;
|
||||
|
||||
buf = cbuf_create(MAX_HISTORY_SIZE);
|
||||
cbuf_append_format(buf, "*** %s: Chat History:\n", cmd->prefix);
|
||||
get_messages(data, maxlines, buf);
|
||||
|
||||
plugin->hub.send_message(plugin, user, cbuf_get(buf));
|
||||
cbuf_destroy(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_error_message(struct plugin_handle* plugin, const char* msg)
|
||||
{
|
||||
plugin->error_msg = msg;
|
||||
}
|
||||
|
||||
static struct chat_history_data* parse_config(const char* line, struct plugin_handle* plugin)
|
||||
{
|
||||
struct chat_history_data* data = (struct chat_history_data*) hub_malloc_zero(sizeof(struct chat_history_data));
|
||||
struct cfg_tokens* tokens = cfg_tokenize(line);
|
||||
char* token = cfg_token_get_first(tokens);
|
||||
|
||||
assert(data != NULL);
|
||||
|
||||
data->history_max = 200;
|
||||
data->history_default = 25;
|
||||
data->history_connect = 5;
|
||||
data->chat_history = list_create();
|
||||
|
||||
while (token)
|
||||
{
|
||||
struct cfg_settings* setting = cfg_settings_split(token);
|
||||
|
||||
if (!setting)
|
||||
{
|
||||
set_error_message(plugin, "Unable to parse startup parameters");
|
||||
cfg_tokens_free(tokens);
|
||||
hub_free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(cfg_settings_get_key(setting), "history_max") == 0)
|
||||
{
|
||||
data->history_max = (size_t) uhub_atoi(cfg_settings_get_value(setting));
|
||||
}
|
||||
else if (strcmp(cfg_settings_get_key(setting), "history_default") == 0)
|
||||
{
|
||||
data->history_default = (size_t) uhub_atoi(cfg_settings_get_value(setting));
|
||||
}
|
||||
else if (strcmp(cfg_settings_get_key(setting), "history_connect") == 0)
|
||||
{
|
||||
data->history_connect = (size_t) uhub_atoi(cfg_settings_get_value(setting));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_error_message(plugin, "Unknown startup parameters given");
|
||||
cfg_tokens_free(tokens);
|
||||
cfg_settings_free(setting);
|
||||
hub_free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cfg_settings_free(setting);
|
||||
token = cfg_token_get_next(tokens);
|
||||
}
|
||||
cfg_tokens_free(tokens);
|
||||
return data;
|
||||
}
|
||||
|
||||
int plugin_register(struct plugin_handle* plugin, const char* config)
|
||||
{
|
||||
struct chat_history_data* data;
|
||||
PLUGIN_INITIALIZE(plugin, "Chat history plugin", "1.0", "Provide a global chat history log.");
|
||||
|
||||
plugin->funcs.on_user_chat_message = history_add;
|
||||
plugin->funcs.on_user_login = user_login;
|
||||
data = parse_config(config, plugin);
|
||||
if (!data)
|
||||
return -1;
|
||||
|
||||
plugin->ptr = data;
|
||||
|
||||
data->command_history_handle = (struct plugin_command_handle*) hub_malloc(sizeof(struct plugin_command_handle));
|
||||
PLUGIN_COMMAND_INITIALIZE(data->command_history_handle, plugin, "history", "?N", auth_cred_guest, &command_history, "Show chat message history.");
|
||||
plugin->hub.command_add(plugin, data->command_history_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plugin_unregister(struct plugin_handle* plugin)
|
||||
{
|
||||
struct chat_history_data* data = (struct chat_history_data*) plugin->ptr;
|
||||
|
||||
if (data)
|
||||
{
|
||||
list_clear(data->chat_history, &hub_free);
|
||||
list_destroy(data->chat_history);
|
||||
|
||||
plugin->hub.command_del(plugin, data->command_history_handle);
|
||||
hub_free(data->command_history_handle);
|
||||
hub_free(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue