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

Added room specific notify settings

This commit is contained in:
James Booth 2015-11-28 00:15:53 +00:00
parent ff2b19e3f9
commit 60305de0d9
7 changed files with 328 additions and 110 deletions

View File

@ -1054,7 +1054,7 @@ static struct cmd_t command_defs[] =
},
{ "/notify",
cmd_notify, parse_args_with_freetext, 2, 4, &cons_notify_setting,
cmd_notify, parse_args_with_freetext, 0, 4, NULL,
CMD_TAGS(
CMD_TAG_UI,
CMD_TAG_CHAT,
@ -1075,6 +1075,9 @@ static struct cmd_t command_defs[] =
"/notify room trigger remove <text>",
"/notify room trigger list",
"/notify room trigger on|off",
"/notify on|off",
"/notify mention on|off",
"/notify trigger on|off"
"/notify remind <seconds>",
"/notify typing on|off",
"/notify typing current on|off",
@ -1084,20 +1087,23 @@ static struct cmd_t command_defs[] =
"Settings for various kinds of desktop notifications.")
CMD_ARGS(
{ "message on|off", "Notifications for regular chat messages." },
{ "message current on|off", "Whether messages in the current window trigger notifications." },
{ "message current on|off", "Whether to show regular chat message notifications when the window is focussed." },
{ "message text on|off", "Show message text in regular message notifications." },
{ "message trigger add <text>", "Notify when specified text included in regular chat message." },
{ "message trigger remove <text>", "Remove regular chat notification for specified text." },
{ "message trigger list", "List all regular chat custom text notifications." },
{ "message trigger on|off", "Enable or disable all regular chat custom text notifications." },
{ "room on|off", "Notifications for chat room messages, mention triggers notifications only when your nick is mentioned." },
{ "room mention on|off", "Notifications for chat room messages when your nick is mentioned." },
{ "room current on|off", "Whether chat room messages in the current window trigger notifications." },
{ "message trigger on|off", "Enable or disable all regular chat notification triggers." },
{ "room on|off", "Notifications for all chat room messages, 'mention' only notifies when your nick is mentioned." },
{ "room mention on|off", "Notifications for all chat room messages when your nick is mentioned." },
{ "room current on|off", "Whether to show all chat room messages notifications when the window is focussed." },
{ "room text on|off", "Show message text in chat room message notifications." },
{ "room trigger add <text>", "Notify when specified text included in regular chat message." },
{ "room trigger remove <text>", "Remove regular chat notification for specified text." },
{ "room trigger list", "List all regular chat custom text notifications." },
{ "room trigger on|off", "Enable or disable all regular chat custom text notifications." },
{ "room trigger add <text>", "Notify when specified text included in all chat room messages." },
{ "room trigger remove <text>", "Remove chat room notification trigger." },
{ "room trigger list", "List all chat room triggers." },
{ "room trigger on|off", "Enable or disable all chat room notification triggers." },
{ "on|off", "Notifications for the current chat room." },
{ "mention on|off", "Override the global 'mention' setting for the current chat room." },
{ "trigger on|off", "Override the global 'trigger' setting for the current chat room." },
{ "remind <seconds>", "Notification reminder period for unread messages, use 0 to disable." },
{ "typing on|off", "Notifications when contacts are typing." },
{ "typing current on|off", "Whether typing notifications are triggered for the current window." },
@ -1942,6 +1948,10 @@ cmd_init(void)
autocomplete_add(notify_ac, "remind");
autocomplete_add(notify_ac, "invite");
autocomplete_add(notify_ac, "sub");
autocomplete_add(notify_ac, "on");
autocomplete_add(notify_ac, "off");
autocomplete_add(notify_ac, "mention");
autocomplete_add(notify_ac, "trigger");
notify_message_ac = autocomplete_new();
autocomplete_add(notify_message_ac, "on");
@ -3178,9 +3188,14 @@ _notify_autocomplete(ProfWin *window, const char *const input)
return result;
}
result = autocomplete_param_with_func(input, "/notify room current", prefs_autocomplete_boolean_choice);
if (result) {
return result;
gchar *boolean_choices1[] = { "/notify room current", "/notify message current", "/notify typing current",
"/notify room text", "/notify room mention", "/notify message text" };
for (i = 0; i < ARRAY_SIZE(boolean_choices1); i++) {
result = autocomplete_param_with_func(input, boolean_choices1[i],
prefs_autocomplete_boolean_choice);
if (result) {
return result;
}
}
result = autocomplete_param_with_ac(input, "/notify room trigger", notify_trigger_ac, TRUE);
@ -3188,36 +3203,11 @@ _notify_autocomplete(ProfWin *window, const char *const input)
return result;
}
result = autocomplete_param_with_func(input, "/notify message current", prefs_autocomplete_boolean_choice);
if (result) {
return result;
}
result = autocomplete_param_with_ac(input, "/notify message trigger", notify_trigger_ac, TRUE);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/notify typing current", prefs_autocomplete_boolean_choice);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/notify room text", prefs_autocomplete_boolean_choice);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/notify room mention", prefs_autocomplete_boolean_choice);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/notify message text", prefs_autocomplete_boolean_choice);
if (result) {
return result;
}
result = autocomplete_param_with_ac(input, "/notify room", notify_room_ac, TRUE);
if (result) {
return result;
@ -3233,9 +3223,9 @@ _notify_autocomplete(ProfWin *window, const char *const input)
return result;
}
gchar *boolean_choices[] = { "/notify invite", "/notify sub" };
for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
result = autocomplete_param_with_func(input, boolean_choices[i],
gchar *boolean_choices2[] = { "/notify invite", "/notify sub", "/notify mention", "/notify trigger"};
for (i = 0; i < ARRAY_SIZE(boolean_choices2); i++) {
result = autocomplete_param_with_func(input, boolean_choices2[i],
prefs_autocomplete_boolean_choice);
if (result) {
return result;

View File

@ -4266,16 +4266,48 @@ cmd_gone(ProfWin *window, const char *const command, gchar **args)
gboolean
cmd_notify(ProfWin *window, const char *const command, gchar **args)
{
char *kind = args[0];
if (!args[0]) {
ProfWin *current = wins_get_current();
if (current->type == WIN_MUC) {
ProfMucWin *mucwin = (ProfMucWin *)current;
gboolean has_notify = prefs_has_room_notify(mucwin->roomjid);
gboolean has_notify_mention = prefs_has_room_notify_mention(mucwin->roomjid);
gboolean has_notify_trigger = prefs_has_room_notify_trigger(mucwin->roomjid);
// bad kind
if ((strcmp(kind, "message") != 0) && (strcmp(kind, "typing") != 0) &&
(strcmp(kind, "remind") != 0) && (strcmp(kind, "invite") != 0) &&
(strcmp(kind, "sub") != 0) && (strcmp(kind, "room") != 0)) {
cons_bad_cmd_usage(command);
if (!has_notify && !has_notify_mention && !has_notify_trigger) {
win_vprintln_ch(window, '!', "No notification settings for %s", mucwin->roomjid);
} else {
win_vprintln_ch(window, '!', "Notification settings for %s", mucwin->roomjid);
if (has_notify) {
if (prefs_get_room_notify(mucwin->roomjid)) {
win_vprintln_ch(window, '!', " All: ON");
} else {
win_vprintln_ch(window, '!', " All: OFF");
}
}
if (has_notify_mention) {
if (prefs_get_room_notify_mention(mucwin->roomjid)) {
win_vprintln_ch(window, '!', " Mention: ON");
} else {
win_vprintln_ch(window, '!', " Mention: OFF");
}
}
if (has_notify_trigger) {
if (prefs_get_room_notify_trigger(mucwin->roomjid)) {
win_vprintln_ch(window, '!', " Triggers: ON");
} else {
win_vprintln_ch(window, '!', " Triggers: OFF");
}
}
}
} else {
cons_notify_setting();
}
return TRUE;
}
// set message setting
} else if (strcmp(kind, "message") == 0) {
// message settings
if (strcmp(args[0], "message") == 0) {
if (strcmp(args[1], "on") == 0) {
cons_show("Message notifications enabled.");
prefs_set_boolean(PREF_NOTIFY_MESSAGE, TRUE);
@ -4351,8 +4383,8 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
cons_show("Usage: /notify message on|off");
}
// set room setting
} else if (strcmp(kind, "room") == 0) {
// chat room settings
} else if (strcmp(args[0], "room") == 0) {
if (strcmp(args[1], "on") == 0) {
cons_show("Room notifications enabled.");
prefs_set_boolean(PREF_NOTIFY_ROOM, TRUE);
@ -4438,8 +4470,8 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
cons_show("Usage: /notify room on|off|mention");
}
// set typing setting
} else if (strcmp(kind, "typing") == 0) {
// typing settings
} else if (strcmp(args[0], "typing") == 0) {
if (strcmp(args[1], "on") == 0) {
cons_show("Typing notifications enabled.");
prefs_set_boolean(PREF_NOTIFY_TYPING, TRUE);
@ -4460,8 +4492,8 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
cons_show("Usage: /notify typing on|off");
}
// set invite setting
} else if (strcmp(kind, "invite") == 0) {
// invite settings
} else if (strcmp(args[0], "invite") == 0) {
if (strcmp(args[1], "on") == 0) {
cons_show("Chat room invite notifications enabled.");
prefs_set_boolean(PREF_NOTIFY_INVITE, TRUE);
@ -4472,8 +4504,8 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
cons_show("Usage: /notify invite on|off");
}
// set subscription setting
} else if (strcmp(kind, "sub") == 0) {
// subscription settings
} else if (strcmp(args[0], "sub") == 0) {
if (strcmp(args[1], "on") == 0) {
cons_show("Subscription notifications enabled.");
prefs_set_boolean(PREF_NOTIFY_SUB, TRUE);
@ -4484,8 +4516,8 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
cons_show("Usage: /notify sub on|off");
}
// set remind setting
} else if (strcmp(kind, "remind") == 0) {
// remind settings
} else if (strcmp(args[0], "remind") == 0) {
gint period = atoi(args[1]);
prefs_set_notify_remind(period);
if (period == 0) {
@ -4496,8 +4528,114 @@ cmd_notify(ProfWin *window, const char *const command, gchar **args)
cons_show("Message reminder period set to %d seconds.", period);
}
// current chat room settings
} else if (g_strcmp0(args[0], "on") == 0) {
jabber_conn_status_t conn_status = jabber_get_connection_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currenlty connected.");
} else {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat room.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
prefs_set_room_notify(mucwin->roomjid, TRUE);
win_vprintln_ch(window, '!', "Notifications enabled for %s", mucwin->roomjid);
}
}
} else if (g_strcmp0(args[0], "off") == 0) {
jabber_conn_status_t conn_status = jabber_get_connection_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currenlty connected.");
} else {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat room.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
prefs_set_room_notify(mucwin->roomjid, FALSE);
win_vprintln_ch(window, '!', "Notifications disabled for %s", mucwin->roomjid);
}
}
} else if (g_strcmp0(args[0], "mention") == 0) {
jabber_conn_status_t conn_status = jabber_get_connection_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currenlty connected.");
} else {
if (g_strcmp0(args[1], "on") == 0) {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat room.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
prefs_set_room_notify_mention(mucwin->roomjid, TRUE);
win_vprintln_ch(window, '!', "Mention notifications enabled for %s", mucwin->roomjid);
}
} else if (g_strcmp0(args[1], "off") == 0) {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat rooms.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
prefs_set_room_notify_mention(mucwin->roomjid, FALSE);
win_vprintln_ch(window, '!', "Mention notifications disabled for %s", mucwin->roomjid);
}
} else {
cons_bad_cmd_usage(command);
}
}
} else if (g_strcmp0(args[0], "trigger") == 0) {
jabber_conn_status_t conn_status = jabber_get_connection_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currenlty connected.");
} else {
if (g_strcmp0(args[1], "on") == 0) {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat room.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
prefs_set_room_notify_trigger(mucwin->roomjid, TRUE);
win_vprintln_ch(window, '!', "Custom trigger notifications enabled for %s", mucwin->roomjid);
}
} else if (g_strcmp0(args[1], "off") == 0) {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat rooms.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
prefs_set_room_notify_trigger(mucwin->roomjid, FALSE);
win_vprintln_ch(window, '!', "Custom trigger notifications disabled for %s", mucwin->roomjid);
}
} else {
cons_bad_cmd_usage(command);
}
}
} else if (g_strcmp0(args[0], "reset") == 0) {
jabber_conn_status_t conn_status = jabber_get_connection_status();
if (conn_status != JABBER_CONNECTED) {
cons_show("You are not currenlty connected.");
} else {
ProfWin *window = wins_get_current();
if (window->type != WIN_MUC) {
cons_show("You must be in a chat room.");
} else {
ProfMucWin *mucwin = (ProfMucWin*)window;
gboolean res = prefs_reset_room_notify(mucwin->roomjid);
if (res) {
win_vprintln_ch(window, '!', "Notification settings set to global defaults for %s", mucwin->roomjid);
} else {
win_vprintln_ch(window, '!', "No custom notification settings for %s", mucwin->roomjid);
}
}
}
} else {
cons_show("Unknown command: %s.", kind);
cons_bad_cmd_usage(command);
}
return TRUE;

View File

@ -215,10 +215,8 @@ prefs_reset_room_trigger_ac(void)
autocomplete_reset(room_trigger_ac);
}
gboolean
prefs_get_notify_chat(gboolean current_win, const char *const message)
prefs_do_chat_notify(gboolean current_win, const char *const message)
{
gboolean notify_current = prefs_get_boolean(PREF_NOTIFY_MESSAGE_CURRENT);
gboolean notify_window = FALSE;
@ -235,36 +233,35 @@ prefs_get_notify_chat(gboolean current_win, const char *const message)
}
gboolean notify_trigger = prefs_get_boolean(PREF_NOTIFY_MESSAGE_TRIGGER);
if (!notify_trigger) {
return FALSE;
}
gboolean trigger_found = FALSE;
char *message_lower = g_utf8_strdown(message, -1);
gsize len = 0;
gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", &len, NULL);
int i;
for (i = 0; i < len; i++) {
char *trigger_lower = g_utf8_strdown(triggers[i], -1);
if (g_strrstr(message_lower, trigger_lower)) {
trigger_found = TRUE;
if (notify_trigger) {
gboolean trigger_found = FALSE;
char *message_lower = g_utf8_strdown(message, -1);
gsize len = 0;
gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "message.trigger.list", &len, NULL);
int i;
for (i = 0; i < len; i++) {
char *trigger_lower = g_utf8_strdown(triggers[i], -1);
if (g_strrstr(message_lower, trigger_lower)) {
trigger_found = TRUE;
g_free(trigger_lower);
break;
}
g_free(trigger_lower);
break;
}
g_free(trigger_lower);
}
g_strfreev(triggers);
g_free(message_lower);
g_strfreev(triggers);
g_free(message_lower);
if (trigger_found) {
return TRUE;
if (trigger_found) {
return TRUE;
}
}
return FALSE;
}
gboolean
prefs_get_notify_room(gboolean current_win, const char *const nick, const char *const message)
prefs_do_room_notify(gboolean current_win, const char *const roomjid, const char *const nick,
const char *const message)
{
gboolean notify_current = prefs_get_boolean(PREF_NOTIFY_ROOM_CURRENT);
gboolean notify_window = FALSE;
@ -275,12 +272,22 @@ prefs_get_notify_room(gboolean current_win, const char *const nick, const char *
return FALSE;
}
gboolean notify_room = prefs_get_boolean(PREF_NOTIFY_ROOM);
gboolean notify_room = FALSE;
if (g_key_file_has_key(prefs, roomjid, "notify", NULL)) {
notify_room = g_key_file_get_boolean(prefs, roomjid, "notify", NULL);
} else {
notify_room = prefs_get_boolean(PREF_NOTIFY_ROOM);
}
if (notify_room) {
return TRUE;
}
gboolean notify_mention = prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION);
gboolean notify_mention = FALSE;
if (g_key_file_has_key(prefs, roomjid, "notify.mention", NULL)) {
notify_mention = g_key_file_get_boolean(prefs, roomjid, "notify.mention", NULL);
} else {
notify_mention = prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION);
}
if (notify_mention) {
char *message_lower = g_utf8_strdown(message, -1);
char *nick_lower = g_utf8_strdown(nick, -1);
@ -293,29 +300,101 @@ prefs_get_notify_room(gboolean current_win, const char *const nick, const char *
g_free(nick_lower);
}
gboolean notify_trigger = prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER);
if (!notify_trigger) {
return FALSE;
gboolean notify_trigger = FALSE;
if (g_key_file_has_key(prefs, roomjid, "notify.trigger", NULL)) {
notify_trigger = g_key_file_get_boolean(prefs, roomjid, "notify.trigger", NULL);
} else {
notify_trigger = prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER);
}
gboolean trigger_found = FALSE;
char *message_lower = g_utf8_strdown(message, -1);
gsize len = 0;
gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
int i;
for (i = 0; i < len; i++) {
char *trigger_lower = g_utf8_strdown(triggers[i], -1);
if (g_strrstr(message_lower, trigger_lower)) {
trigger_found = TRUE;
if (notify_trigger) {
gboolean trigger_found = FALSE;
char *message_lower = g_utf8_strdown(message, -1);
gsize len = 0;
gchar **triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
int i;
for (i = 0; i < len; i++) {
char *trigger_lower = g_utf8_strdown(triggers[i], -1);
if (g_strrstr(message_lower, trigger_lower)) {
trigger_found = TRUE;
g_free(trigger_lower);
break;
}
g_free(trigger_lower);
break;
}
g_free(trigger_lower);
}
g_strfreev(triggers);
g_free(message_lower);
g_strfreev(triggers);
g_free(message_lower);
if (trigger_found) {
if (trigger_found) {
return TRUE;
}
}
return FALSE;
}
void
prefs_set_room_notify(const char *const roomjid, gboolean value)
{
g_key_file_set_boolean(prefs, roomjid, "notify", value);
_save_prefs();
}
void
prefs_set_room_notify_mention(const char *const roomjid, gboolean value)
{
g_key_file_set_boolean(prefs, roomjid, "notify.mention", value);
_save_prefs();
}
void
prefs_set_room_notify_trigger(const char *const roomjid, gboolean value)
{
g_key_file_set_boolean(prefs, roomjid, "notify.trigger", value);
_save_prefs();
}
gboolean
prefs_has_room_notify(const char *const roomjid)
{
return g_key_file_has_key(prefs, roomjid, "notify", NULL);
}
gboolean
prefs_has_room_notify_mention(const char *const roomjid)
{
return g_key_file_has_key(prefs, roomjid, "notify.mention", NULL);
}
gboolean
prefs_has_room_notify_trigger(const char *const roomjid)
{
return g_key_file_has_key(prefs, roomjid, "notify.trigger", NULL);
}
gboolean
prefs_get_room_notify(const char *const roomjid)
{
return g_key_file_get_boolean(prefs, roomjid, "notify", NULL);
}
gboolean
prefs_get_room_notify_mention(const char *const roomjid)
{
return g_key_file_get_boolean(prefs, roomjid, "notify.mention", NULL);
}
gboolean
prefs_get_room_notify_trigger(const char *const roomjid)
{
return g_key_file_get_boolean(prefs, roomjid, "notify.trigger", NULL);
}
gboolean
prefs_reset_room_notify(const char *const roomjid)
{
if (g_key_file_has_group(prefs, roomjid)) {
g_key_file_remove_group(prefs, roomjid, NULL);
_save_prefs();
return TRUE;
}

View File

@ -214,7 +214,18 @@ char* prefs_get_string(preference_t pref);
void prefs_free_string(char *pref);
void prefs_set_string(preference_t pref, char *value);
gboolean prefs_get_notify_chat(gboolean current_win, const char *const message);
gboolean prefs_get_notify_room(gboolean current_win, const char *const nick, const char *const message);
gboolean prefs_do_chat_notify(gboolean current_win, const char *const message);
gboolean prefs_do_room_notify(gboolean current_win, const char *const roomjid, const char *const nick, const char *const message);
void prefs_set_room_notify(const char *const roomjid, gboolean value);
void prefs_set_room_notify_mention(const char *const roomjid, gboolean value);
void prefs_set_room_notify_trigger(const char *const roomjid, gboolean value);
gboolean prefs_reset_room_notify(const char *const roomjid);
gboolean prefs_has_room_notify(const char *const roomjid);
gboolean prefs_has_room_notify_mention(const char *const roomjid);
gboolean prefs_has_room_notify_trigger(const char *const roomjid);
gboolean prefs_get_room_notify(const char *const roomjid);
gboolean prefs_get_room_notify_mention(const char *const roomjid);
gboolean prefs_get_room_notify_trigger(const char *const roomjid);
#endif

View File

@ -240,7 +240,7 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
char *display_name = roster_get_msg_display_name(chatwin->barejid, resource);
gboolean is_current = wins_is_current(window);
gboolean notify = prefs_get_notify_chat(is_current, message);
gboolean notify = prefs_do_chat_notify(is_current, message);
// currently viewing chat window with sender
if (wins_is_current(window)) {

View File

@ -375,7 +375,7 @@ mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const mes
}
gboolean is_current = wins_is_current(window);
gboolean notify = prefs_get_notify_room(is_current, my_nick, message);
gboolean notify = prefs_do_room_notify(is_current, mucwin->roomjid, my_nick, message);
// currently in groupchat window
if (wins_is_current(window)) {

View File

@ -54,7 +54,7 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
char *display_from = get_nick_from_full_jid(privatewin->fulljid);
gboolean is_current = wins_is_current(window);
gboolean notify = prefs_get_notify_chat(is_current, message);
gboolean notify = prefs_do_chat_notify(is_current, message);
// currently viewing chat window with sender
if (wins_is_current(window)) {