diff --git a/src/chat/chat_poll.c b/src/chat/chat_poll.c index dce0e833..e3264141 100644 --- a/src/chat/chat_poll.c +++ b/src/chat/chat_poll.c @@ -342,7 +342,7 @@ int chat_feedback_command(struct UTUN_INSTANCE* inst, const char* command, char* answer.count = n; bytes = chat_answer_encode(&answer, data, sizeof(data)); kind = CHAT_FEEDBACK_ANSWER; if (!poll_answer_allowed(&poll, &answer)) return poll_command_error("Answer violates poll rules", result); } else if (!strcmp(cmd, "react")) { - uint8_t ids[CHAT_REACTION_SELECTED_MAX + 1]; size_t count = 0; + uint8_t ids[CHAT_REACTION_SELECTED_MAX]; size_t count = 0; int clear = poll_field_bool(inst, command, "$.clear", 0); if (clear < 0) return poll_command_error("Invalid clear flag", result); if (!clear) { struct chat_feedback_record old; @@ -359,13 +359,11 @@ int chat_feedback_command(struct UTUN_INSTANCE* inst, const char* command, char* ok = type && (!strcmp(type, "integer") || !strcmp(type, "text")) && emoji >= 0 && emoji < CHAT_REACTION_COUNT; sqlite3_finalize(st); if (!ok) return poll_command_error("Invalid emoji", result); - size_t at = 0; while (at < count && ids[at] != emoji) at++; - int on = poll_field_bool(inst, command, "$.on", at == count); + int selected = count == 1 && ids[0] == emoji; + int on = poll_field_bool(inst, command, "$.on", !selected); if (on < 0) return poll_command_error("Invalid reaction state", result); - if (on && at == count) { - if (count == CHAT_REACTION_SELECTED_MAX) return poll_command_error("Too many reactions", result); - ids[count++] = emoji; - } else if (!on && at < count) { memmove(ids + at, ids + at + 1, count - at - 1); count--; } + if (on) { ids[0] = (uint8_t)emoji; count = 1; } + else if (selected) count = 0; } bytes = chat_reactions_encode(ids, count, data, sizeof(data)); kind = CHAT_FEEDBACK_REACTION; } else return poll_command_error("Unknown interaction command", result); diff --git a/src/chat/chat_poll.h b/src/chat/chat_poll.h index 15aab678..51a4ad69 100644 --- a/src/chat/chat_poll.h +++ b/src/chat/chat_poll.h @@ -1,6 +1,6 @@ /* Общий формат опросов, ответов и реакций. Подписанное poll-сообщение неизменяемо. * single допускает один пункт ИЛИ свой ответ; multiple — пункты и необязательный свой ответ. - * Пустой ответ/набор реакций кодируется нулевой длиной и сохраняется с новым ver. + * Один пользователь выбирает одну реакцию. Отмена/пустой ответ кодируются нулевой длиной с новым ver. * Все сетевые/DB API вызываются в uasync; кодеки не зависят от экземпляра. * JSON-снимки и результаты команд выделяются через u_malloc, освобождаются u_free. */ #ifndef CHAT_POLL_H @@ -23,7 +23,7 @@ struct UTUN_INSTANCE; #define CHAT_POLL_CUSTOM_MAX 1024 #define CHAT_POLL_WIRE_MAX 2048 #define CHAT_REACTION_COUNT 16 -#define CHAT_REACTION_SELECTED_MAX 8 +#define CHAT_REACTION_SELECTED_MAX 1 struct chat_poll { uint8_t mode, allow_custom_answer, option_count; @@ -55,7 +55,7 @@ int chat_feedback_snapshot(struct UTUN_INSTANCE* inst, const char* channel, cons /* JSON-команды poll_create, vote, react, feedback, reaction_catalog. 0/-1. * Ошибка возвращает JSON с error; успех — снимок или каталог. * vote: choices:[0..31], custom:string; пустые значения отменяют голос. - * react: emoji:0..15, on:boolean необязателен (без него переключить), clear:true снимает все. */ + * react: emoji:0..15; новая реакция заменяет прежнюю. Без on — переключить выбранную, clear:true — отменить. */ int chat_feedback_command(struct UTUN_INSTANCE* inst, const char* command, char** result); /* Проверка отложенных ответов и диагностика при получении сообщения/нового состояния. */ void chat_feedback_check_message(struct UTUN_INSTANCE* inst, const char* channel, const uint8_t message_id[32]);