1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-12-04 14:46:46 -05:00

Removed chat states from chat_session module, WIP

This commit is contained in:
James Booth 2015-01-08 00:57:25 +00:00
parent 8bbf126e83
commit 5e7d2f5f91
10 changed files with 223 additions and 398 deletions

21
TODO_STATES Normal file
View File

@ -0,0 +1,21 @@
Chat sessions
Start (if not already started):
When message received from fulljid
When active, composing, paused or inactive chat state received from fulljid
End:
When message received from a different fulljid (start new session)
When active, composing, paused or inactive received from different fulljid (start new session)
When any presence received from any resource
When gone received from fulljid
When window closed
When gone sent
Chat states
Active - focus chat window, or message send
Composing - typing in chat session (window)
Paused - no typing for 30 seconds
Inactive - unfocus chat window, or after 2 mins
Gone - close chat window, or after 10 mins

View File

@ -43,45 +43,20 @@
#include "log.h"
#include "xmpp/xmpp.h"
#define PAUSED_TIMOUT 10.0
#define INACTIVE_TIMOUT 30.0
typedef enum {
CHAT_STATE_STARTED,
CHAT_STATE_ACTIVE,
CHAT_STATE_PAUSED,
CHAT_STATE_COMPOSING,
CHAT_STATE_INACTIVE,
CHAT_STATE_GONE
} chat_state_t;
typedef struct chat_session_t {
char *barejid;
char *resource;
gboolean send_states;
chat_state_t state;
GTimer *active_timer;
gboolean sent;
} ChatSession;
static GHashTable *sessions;
static ChatSession*
static void
_chat_session_new(const char * const barejid, const char * const resource, gboolean send_states)
{
assert(barejid != NULL);
assert(resource != NULL);
ChatSession *new_session = malloc(sizeof(struct chat_session_t));
new_session->barejid = strdup(barejid);
if (resource) {
new_session->resource = strdup(resource);
} else {
new_session->resource = NULL;
}
new_session->resource = strdup(resource);
new_session->send_states = send_states;
new_session->state = CHAT_STATE_STARTED;
new_session->active_timer = g_timer_new();
new_session->sent = FALSE;
return new_session;
g_hash_table_insert(sessions, strdup(barejid), new_session);
}
static void
@ -90,10 +65,6 @@ _chat_session_free(ChatSession *session)
if (session != NULL) {
free(session->barejid);
free(session->resource);
if (session->active_timer != NULL) {
g_timer_destroy(session->active_timer);
session->active_timer = NULL;
}
free(session);
}
}
@ -112,284 +83,34 @@ chat_sessions_clear(void)
g_hash_table_remove_all(sessions);
}
gboolean
chat_session_exists(const char * const barejid)
ChatSession*
chat_session_get(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
return (session != NULL);
}
char*
chat_session_get_resource(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
assert(session != NULL);
return session->resource;
}
gboolean
chat_session_send_states(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
assert(session != NULL);
return session->send_states;
return g_hash_table_lookup(sessions, barejid);
}
void
chat_session_on_incoming_message(const char * const barejid, const char * const resource, gboolean send_states)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
GString *log_msg = g_string_new("");
// no session exists, create one
if (!session) {
g_string_append(log_msg, "Creating chat session for ");
g_string_append(log_msg, barejid);
if (resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, resource);
}
session = _chat_session_new(barejid, resource, send_states);
g_hash_table_insert(sessions, strdup(barejid), session);
// session exists for different resource, replace session
} else if (g_strcmp0(session->resource, resource) != 0) {
g_string_append(log_msg, "Replacing chat session for ");
g_string_append(log_msg, barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
g_string_append(log_msg, " with session for ");
g_string_append(log_msg, barejid);
if (resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, resource);
}
g_hash_table_remove(sessions, session);
session = _chat_session_new(barejid, resource, send_states);
g_hash_table_insert(sessions, strdup(barejid), session);
// session exists for resource, update state
} else {
g_string_append(log_msg, "Updating chat session for ");
g_string_append(log_msg, session->barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
session->send_states = send_states;
}
if (send_states) {
g_string_append(log_msg, ", chat states supported");
} else {
g_string_append(log_msg, ", chat states not supported");
}
log_debug(log_msg->str);
g_string_free(log_msg, TRUE);
}
void
chat_session_on_message_send(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
// if no session exists, create one with no resource, and send states
if (!session) {
log_debug("Creating chat session for %s, chat states supported", barejid);
session = _chat_session_new(barejid, NULL, TRUE);
g_hash_table_insert(sessions, strdup(barejid), session);
}
session->state = CHAT_STATE_ACTIVE;
g_timer_start(session->active_timer);
session->sent = TRUE;
}
void
chat_session_on_window_close(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
if (session) {
if (prefs_get_boolean(PREF_STATES) && session->send_states) {
GString *jid = g_string_new(session->barejid);
if (session->resource) {
g_string_append(jid, "/");
g_string_append(jid, session->resource);
}
message_send_gone(jid->str);
g_string_free(jid, TRUE);
}
GString *log_msg = g_string_new("Removing chat session for ");
g_string_append(log_msg, barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
log_debug(log_msg->str);
g_string_free(log_msg, TRUE);
g_hash_table_remove(sessions, barejid);
}
}
void
chat_session_on_offline(const char * const barejid, const char * const resource)
{
if (!resource) {
return;
}
ChatSession *session = g_hash_table_lookup(sessions, barejid);
if (session && (g_strcmp0(session->resource, resource) == 0)) {
GString *log_msg = g_string_new("Removing chat session for ");
g_string_append(log_msg, barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
log_debug(log_msg->str);
g_string_free(log_msg, TRUE);
g_hash_table_remove(sessions, barejid);
}
}
void
chat_session_on_gone(const char * const barejid)
chat_session_on_recipient_activity(const char * const barejid, const char * const resource)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
if (session) {
GString *log_msg = g_string_new("Removing chat session for ");
g_string_append(log_msg, barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
log_debug(log_msg->str);
g_string_free(log_msg, TRUE);
g_hash_table_remove(sessions, barejid);
}
}
void
chat_session_on_activity(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
if (!session) {
log_debug("Creating chat session for %s, chat states supported", barejid);
session = _chat_session_new(barejid, NULL, TRUE);
g_hash_table_insert(sessions, strdup(barejid), session);
}
if (session->state != CHAT_STATE_COMPOSING) {
session->sent = FALSE;
}
session->state = CHAT_STATE_COMPOSING;
g_timer_start(session->active_timer);
if (!session->sent || session->state == CHAT_STATE_PAUSED) {
if (prefs_get_boolean(PREF_STATES) && prefs_get_boolean(PREF_OUTTYPE) && session->send_states) {
GString *jid = g_string_new(session->barejid);
if (session->resource) {
g_string_append(jid, "/");
g_string_append(jid, session->resource);
}
message_send_composing(jid->str);
g_string_free(jid, TRUE);
}
session->sent = TRUE;
}
}
void
chat_session_on_inactivity(const char * const barejid)
{
ChatSession *session = g_hash_table_lookup(sessions, barejid);
if (!session) {
return;
}
if (session->active_timer != NULL) {
gdouble elapsed = g_timer_elapsed(session->active_timer, NULL);
if ((prefs_get_gone() != 0) && (elapsed > (prefs_get_gone() * 60.0))) {
if (session->state != CHAT_STATE_GONE) {
session->sent = FALSE;
}
session->state = CHAT_STATE_GONE;
} else if (elapsed > INACTIVE_TIMOUT) {
if (session->state != CHAT_STATE_INACTIVE) {
session->sent = FALSE;
}
session->state = CHAT_STATE_INACTIVE;
} else if (elapsed > PAUSED_TIMOUT) {
if (session->state == CHAT_STATE_COMPOSING) {
session->sent = FALSE;
session->state = CHAT_STATE_PAUSED;
}
}
}
if (session->sent == FALSE) {
GString *jid = g_string_new(session->barejid);
if (session->resource) {
g_string_append(jid, "/");
g_string_append(jid, session->resource);
}
if (session->state == CHAT_STATE_GONE) {
if (prefs_get_boolean(PREF_STATES) && session->send_states) {
message_send_gone(jid->str);
}
session->sent = TRUE;
GString *log_msg = g_string_new("Removing chat session for ");
g_string_append(log_msg, barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
log_debug(log_msg->str);
g_string_free(log_msg, TRUE);
// session exists with resource, do nothing
if (g_strcmp0(session->resource, resource) == 0) {
return;
// session exists with differet resource, replace
} else {
g_hash_table_remove(sessions, barejid);
} else if (session->state == CHAT_STATE_INACTIVE) {
if (prefs_get_boolean(PREF_STATES) && session->send_states) {
message_send_inactive(jid->str);
}
session->sent = TRUE;
} else if (session->state == CHAT_STATE_PAUSED && prefs_get_boolean(PREF_OUTTYPE)) {
if (prefs_get_boolean(PREF_STATES) && session->send_states) {
message_send_paused(jid->str);
}
session->sent = TRUE;
_chat_session_new(barejid, resource, FALSE);
}
g_string_free(jid, TRUE);
// no session, create one
} else {
_chat_session_new(barejid, resource, FALSE);
}
}
void
chat_session_on_cancel(const char * const jid)
chat_session_remove(const char * const barejid)
{
Jid *jidp = jid_create(jid);
if (jidp) {
ChatSession *session = g_hash_table_lookup(sessions, jidp->barejid);
if (session) {
GString *log_msg = g_string_new("Removing chat session for ");
g_string_append(log_msg, jidp->barejid);
if (session->resource) {
g_string_append(log_msg, "/");
g_string_append(log_msg, session->resource);
}
log_debug(log_msg->str);
g_string_free(log_msg, TRUE);
g_hash_table_remove(sessions, jidp->barejid);
}
}
g_hash_table_remove(sessions, barejid);
}

View File

@ -37,21 +37,17 @@
#include <glib.h>
typedef struct chat_session_t {
char *barejid;
char *resource;
gboolean send_states;
} ChatSession;
void chat_sessions_init(void);
void chat_sessions_clear(void);
gboolean chat_session_exists(const char * const barejid);
char* chat_session_get_resource(const char * const barejid);
gboolean chat_session_send_states(const char * const barejid);
void chat_session_on_message_send(const char * const barejid);
void chat_session_on_window_close(const char * const barejid);
void chat_session_on_incoming_message(const char * const barejid, const char * const resource, gboolean send_states);
void chat_session_on_offline(const char * const barejid, const char * const resource);
void chat_session_on_cancel(const char * const jid);
void chat_session_on_gone(const char * const barejid);
void chat_session_on_activity(const char * const barejid);
void chat_session_on_inactivity(const char * const barejid);
ChatSession* chat_session_get(const char * const barejid);
void chat_session_on_recipient_activity(const char * const barejid, const char * const resourcepart);
void chat_session_remove(const char * const barejid);
#endif

52
src/chat_state.c Normal file
View File

@ -0,0 +1,52 @@
/*
* chat_state.c
*
* Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity 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.
*
* Profanity 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 Profanity. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
*/
// TODO make preferences
#define PAUSED_SECS 30.0
#define INACTIVE_SECS 120.0
typedef enum {
CHAT_STATE_ACTIVE,
CHAT_STATE_PAUSED,
CHAT_STATE_COMPOSING,
CHAT_STATE_INACTIVE,
CHAT_STATE_GONE
} chat_state_type_t;
typedef struct chat_state_t {
chat_state_t state;
GTimer *active_timer;
gboolean sent;
} ChatState;

37
src/chat_state.h Normal file
View File

@ -0,0 +1,37 @@
/*
* chat_state.h
*
* Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity 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.
*
* Profanity 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 Profanity. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
*/
#ifndef CHAT_STATE_H
#define CHAT_STATE_H

View File

@ -131,38 +131,38 @@ prof_run(const int disable_tls, char *log_level, char *account_name)
g_timer_destroy(timer);
}
void
prof_handle_idle(void)
{
jabber_conn_status_t status = jabber_get_connection_status();
if (status == JABBER_CONNECTED) {
GSList *recipients = ui_get_chat_recipients();
GSList *curr = recipients;
while (curr != NULL) {
char *barejid = curr->data;
chat_session_on_inactivity(barejid);
curr = g_slist_next(curr);
}
if (recipients != NULL) {
g_slist_free(recipients);
}
}
}
void
prof_handle_activity(void)
{
win_type_t win_type = ui_current_win_type();
jabber_conn_status_t status = jabber_get_connection_status();
if ((status == JABBER_CONNECTED) && (win_type == WIN_CHAT)) {
ProfChatWin *chatwin = wins_get_current_chat();
chat_session_on_activity(chatwin->barejid);
}
}
//
//void
//prof_handle_idle(void)
//{
// jabber_conn_status_t status = jabber_get_connection_status();
// if (status == JABBER_CONNECTED) {
// GSList *recipients = ui_get_chat_recipients();
// GSList *curr = recipients;
//
// while (curr != NULL) {
// char *barejid = curr->data;
// chat_session_on_inactivity(barejid);
// curr = g_slist_next(curr);
// }
//
// if (recipients != NULL) {
// g_slist_free(recipients);
// }
// }
//}
//
//void
//prof_handle_activity(void)
//{
// win_type_t win_type = ui_current_win_type();
// jabber_conn_status_t status = jabber_get_connection_status();
//
// if ((status == JABBER_CONNECTED) && (win_type == WIN_CHAT)) {
// ProfChatWin *chatwin = wins_get_current_chat();
// chat_session_on_activity(chatwin->barejid);
// }
//}
/*
* Take a line of input and process it, return TRUE if profanity is to

View File

@ -87,7 +87,8 @@ handle_message_error(const char * const jid, const char * const type,
// handle recipient not found ('from' contains a value and type is 'cancel')
} else if (type != NULL && (strcmp(type, "cancel") == 0)) {
log_info("Recipient %s not found: %s", jid, err_msg);
chat_session_on_cancel(jid);
Jid *jidp = jid_create(jid);
chat_session_remove(jidp->barejid);
// handle any other error from recipient
} else {
@ -393,7 +394,7 @@ handle_typing(char *from)
void
handle_gone(const char * const from)
{
chat_session_on_gone(from);
chat_session_remove(from);
ui_recipient_gone(from);
}
@ -464,7 +465,7 @@ handle_contact_offline(char *barejid, char *resource, char *status)
}
rosterwin_roster();
chat_session_on_offline(barejid, resource);
chat_session_remove(barejid);
}
void
@ -507,6 +508,7 @@ handle_contact_online(char *barejid, Resource *resource,
}
rosterwin_roster();
chat_session_remove(barejid);
}
void

View File

@ -686,9 +686,7 @@ ui_close_connected_win(int index)
otr_end_session(chatwin->barejid);
}
#endif
if (chat_session_exists(chatwin->barejid)) {
chat_session_on_window_close(chatwin->barejid);
}
chat_session_remove(chatwin->barejid);
}
}
}
@ -1145,9 +1143,7 @@ ui_prune_wins(void)
if (window->type == WIN_CHAT) {
if (conn_status == JABBER_CONNECTED) {
ProfChatWin *chatwin = (ProfChatWin*)window;
if (chat_session_exists(chatwin->barejid)) {
chat_session_on_window_close(chatwin->barejid);
}
chat_session_remove(chatwin->barejid);
}
}

View File

@ -145,18 +145,18 @@ inp_get_char(char *input, int *size, int *result)
noecho();
*result = wget_wch(inp_win, &ch);
gboolean in_command = FALSE;
if ((display_size > 0 && input[0] == '/') ||
(display_size == 0 && ch == '/')) {
in_command = TRUE;
}
// gboolean in_command = FALSE;
// if ((display_size > 0 && input[0] == '/') ||
// (display_size == 0 && ch == '/')) {
// in_command = TRUE;
// }
if (*result == ERR) {
prof_handle_idle();
}
if ((*result != ERR) && (*result != KEY_CODE_YES) && !in_command && _printable(ch)) {
prof_handle_activity();
}
// if (*result == ERR) {
// prof_handle_idle();
// }
// if ((*result != ERR) && (*result != KEY_CODE_YES) && !in_command && _printable(ch)) {
// prof_handle_activity();
// }
// if it wasn't an arrow key etc
if (!_handle_edit(*result, ch, input, size)) {

View File

@ -86,25 +86,17 @@ message_send_chat(const char * const barejid, const char * const msg)
xmpp_conn_t * const conn = connection_get_conn();
xmpp_ctx_t * const ctx = connection_get_ctx();
chat_session_on_message_send(barejid);
char *resource = chat_session_get_resource(barejid);
gboolean send_state = chat_session_send_states(barejid);
GString *jid = g_string_new(barejid);
if (resource) {
g_string_append(jid, "/");
g_string_append(jid, resource);
}
if (prefs_get_boolean(PREF_STATES) && send_state) {
message = stanza_create_message(ctx, jid->str, STANZA_TYPE_CHAT, msg, STANZA_NAME_ACTIVE);
ChatSession *session = chat_session_get(barejid);
if (session) {
Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
message = stanza_create_message(ctx, jidp->fulljid, STANZA_TYPE_CHAT, msg, NULL);
jid_destroy(jidp);
} else {
message = stanza_create_message(ctx, jid->str, STANZA_TYPE_CHAT, msg, NULL);
message = stanza_create_message(ctx, barejid, STANZA_TYPE_CHAT, msg, NULL);
}
xmpp_send(conn, message);
xmpp_stanza_release(message);
g_string_free(jid, TRUE);
}
void
@ -469,28 +461,36 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
GTimeVal tv_stamp;
gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
// deal with chat states if recipient supports them
if (!delayed) {
// determine chatstate support of recipient
if (stanza_contains_chat_state(stanza)) {
chat_session_on_incoming_message(jid->barejid, jid->resourcepart, TRUE);
// handle chat sessions
if (!delayed && jid->resourcepart) {
gboolean recipient_gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
if (recipient_gone) {
chat_session_remove(jid->barejid);
} else {
chat_session_on_incoming_message(jid->barejid, jid->resourcepart, FALSE);
}
if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL) {
handle_typing(jid->barejid);
} else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL) {
handle_gone(jid->barejid);
} else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL) {
// do something
} else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL) {
// do something
} else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ACTIVE) != NULL) {
// do something
chat_session_on_recipient_activity(jid->barejid, jid->resourcepart);
}
}
// // determine chatstate support of recipient
// if (stanza_contains_chat_state(stanza)) {
// chat_session_on_incoming_message(jid->barejid, jid->resourcepart, TRUE);
// } else {
// chat_session_on_incoming_message(jid->barejid, jid->resourcepart, FALSE);
// }
//
// if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL) {
// handle_typing(jid->barejid);
// } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL) {
// handle_gone(jid->barejid);
// } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL) {
// // do something
// } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL) {
// // do something
// } else if (xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ACTIVE) != NULL) {
// // do something
// }
// }
// check for and deal with message
xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
if (body != NULL) {