diff --git a/assets/HypertextPages/chat-members.nytl.html b/assets/HypertextPages/chat-members.nytl.html index 7323b95..a3a6107 100644 --- a/assets/HypertextPages/chat-members.nytl.html +++ b/assets/HypertextPages/chat-members.nytl.html @@ -4,6 +4,7 @@ + diff --git a/assets/HypertextPages/chat.nytl.html b/assets/HypertextPages/chat.nytl.html index 46f9845..7ccffff 100644 --- a/assets/HypertextPages/chat.nytl.html +++ b/assets/HypertextPages/chat.nytl.html @@ -13,6 +13,7 @@ + Chat diff --git a/assets/HypertextPages/edit-profile.nytl.html b/assets/HypertextPages/edit-profile.nytl.html index 3a7b202..745656f 100644 --- a/assets/HypertextPages/edit-profile.nytl.html +++ b/assets/HypertextPages/edit-profile.nytl.html @@ -4,6 +4,7 @@ + Edit user Profile diff --git a/assets/HypertextPages/err-404.html b/assets/HypertextPages/err-404.html index a54d561..3afea8b 100644 --- a/assets/HypertextPages/err-404.html +++ b/assets/HypertextPages/err-404.html @@ -2,6 +2,7 @@ + Not found diff --git a/assets/HypertextPages/list-rooms.nytl.html b/assets/HypertextPages/list-rooms.nytl.html index 40fc988..e9959f9 100644 --- a/assets/HypertextPages/list-rooms.nytl.html +++ b/assets/HypertextPages/list-rooms.nytl.html @@ -5,6 +5,7 @@ List of chat rooms + diff --git a/assets/HypertextPages/login.nytl.html b/assets/HypertextPages/login.nytl.html index bbc1372..53813fc 100644 --- a/assets/HypertextPages/login.nytl.html +++ b/assets/HypertextPages/login.nytl.html @@ -4,6 +4,7 @@ + Login Page diff --git a/assets/HypertextPages/view-profile.nytl.html b/assets/HypertextPages/view-profile.nytl.html index f2a81ec..ca59638 100644 --- a/assets/HypertextPages/view-profile.nytl.html +++ b/assets/HypertextPages/view-profile.nytl.html @@ -4,6 +4,7 @@ + User Profile diff --git a/assets/css/chat-members.css b/assets/css/chat-members.css index 7509abb..af0d1c6 100644 --- a/assets/css/chat-members.css +++ b/assets/css/chat-members.css @@ -1,6 +1,7 @@ #CM-btn-add { margin-top: 6px; margin-bottom: 4px; + display: none; } .CM-member-box { diff --git a/assets/img/favicon.png b/assets/img/favicon.png new file mode 100644 index 0000000..614d564 Binary files /dev/null and b/assets/img/favicon.png differ diff --git a/assets/js/chat-members.js b/assets/js/chat-members.js index a87e930..1a77952 100644 --- a/assets/js/chat-members.js +++ b/assets/js/chat-members.js @@ -3,7 +3,8 @@ let LocalHistoryId = 0; function genSentBase(){ return { 'chatUpdReq': { - 'LocalHistoryId': LocalHistoryId + 'LocalHistoryId': LocalHistoryId, + 'chatId': openedchat.id } }; } @@ -15,7 +16,7 @@ let myRoleHere = null; // Dung local state updates should be updated first let userDeletionWinStoredUId = -1; function shouldShowDeleteButton(memberSt){ - return userinfo.uid !== memberSt.userId && myRoleHere === userChatRoleAdmin; + return userinfo.uid !== memberSt.userId && myRoleHere === userChatRoleAdmin && memberSt.roleHere !== userChatRoleDeleted; } function updateBoxWithSt(box, memberSt){ @@ -60,9 +61,9 @@ function convertMemberStToBox(memberSt){ if (ev.button !== 0) return; userDeletionWinStoredUId = ID; - activatePopupWindowById("user-deletion-win"); document.getElementById("user-deletion-win-title").innerText = "Do you really want to kick user " + memberSt.nickname + "?"; + activatePopupWindowById("user-deletion-win"); }; box.querySelector(".CM-member-box-leave-btn").style.display = (shouldShowDeleteButton(memberSt) ? "block" : "none"); @@ -77,29 +78,114 @@ function updateLocalStateFromChatUpdResp(chatUpdResp){ // We ignore messages and everything related to them. Dang, I really should add an argument to disable message lookup here // let haveToUpdateAllBoxes = false; for (let memberSt of chatUpdResp.members){ - if (memberSt.id === userinfo.uid && myRoleHere !== memberSt.roleHere){ + console.log([memberSt, userinfo.uid, myRoleHere]); + if (memberSt.userId === userinfo.uid && myRoleHere !== memberSt.roleHere){ myRoleHere = memberSt.roleHere; // haveToUpdateAllBoxes = true; for (let [id, memberSt] of members){ let box = memberBoxes.get(id); updateBoxWithSt(box, memberSt); } + document.getElementById("CM-btn-add").style.display = + (memberSt.roleHere === userChatRoleAdmin ? "block" : "none"); + console.log("DEBUG " + (memberSt.roleHere === userChatRoleAdmin ? "block" : "none")); break; } } for (let memberSt of chatUpdResp.members){ let id = memberSt.userId; - // todo: CONTINUE FROM HERE WHEN YOU WAKE UP + if (members.has(id)){ + updateBoxWithSt(memberBoxes.get(id), memberSt); + } else { + if (memberSt.roleHere !== userChatRoleDeleted){ + members.set(id, memberSt); + let box = convertMemberStToBox(memberSt); + memberBoxes.set(id, box); + literalMemberList.appendChild(box); + } + } } } +function updateLocalStateFromRecv(Recv){ + updateLocalStateFromChatUpdResp(Recv.chatUpdResp); +} + +function configureSummonUserInterface(){ + document.getElementById("user-summoning-yes").onclick = function(ev ){ + if (ev.button !==0) + return; + let nickname = document.getElementById("summoned-user-nickname").value; + let isReadOnly = document.getElementById("summoned-user-is-read-only").checked; + deactivateActivePopup(); + let Sent = genSentBase(); + Sent.nickname = nickname; + Sent.makeReadOnly = Boolean(isReadOnly); + apiRequest("addMemberToChat", Sent). + then((Recv) => { + updateLocalStateFromRecv(Recv); + }).catch((e) => { + console.log(e); + alert("Failed to add user to chat"); + }); + }; + + document.getElementById("user-summoning-no").onclick = function (ev) { + if (ev.button !== 0) + return; + deactivateActivePopup(); + }; + + document.getElementById("CM-btn-add").onclick = function(ev) { + if (ev.button !== 0) + return; + document.getElementById("summoned-user-nickname").value = ""; + // read-only flag persists throughout user summoning sessions, and IT IS NOT A BUG + activatePopupWindowById("user-summoning-win"); + }; +} + +/* Popup activation button is configured for each box separately */ +function configureKickUserInterfaceWinPart(){ + document.getElementById("user-deletion-yes").onclick = function (ev){ + if (ev.button !== 0) + return; + deactivateActivePopup(); + if (userDeletionWinStoredUId < 0) + throw new Error("Karaul"); + let Sent = genSentBase(); + Sent.userId = userDeletionWinStoredUId; + apiRequest("removeMemberFromChat", Sent). + then((Recv) => { + updateLocalStateFromRecv(Recv); + }).catch((e) => { + console.log(e); + alert("Failed to kick user from chat"); + }); + } + + document.getElementById("user-deletion-no").onclick = function (ev) { + if (ev.button !== 0) + return; + deactivateActivePopup(); + }; +} + __mainloopDelayMS = 5000; __guestMainloopPollerAction = function (){ console.log("Hello, world"); + let Sent = genSentBase(); + apiRequest("chatPollEvents", Sent). + then((Recv) => { + console.log(Recv); + updateLocalStateFromRecv(Recv); + }); } window.onload = function(){ console.log("Page loaded"); + configureSummonUserInterface(); + configureKickUserInterfaceWinPart(); updateLocalStateFromChatUpdResp(initial_chatUpdResp); mainloopPoller(); } diff --git a/assets/js/list-rooms.js b/assets/js/list-rooms.js index c464c01..f17e2f1 100644 --- a/assets/js/list-rooms.js +++ b/assets/js/list-rooms.js @@ -19,6 +19,10 @@ function youAreXHere(myRoleHere){ let chatRenunciationWinStoredId = -1; +function shouldShowDeleteButton(myMembershipSt){ + return myMembershipSt.myRoleHere === userChatRoleDeleted; +} + /* Updating chat html box after myMembershipSt in it was updated */ function updateBoxWithNewSt(box, myMembershipSt){ let ID = myMembershipSt.chatId; @@ -26,10 +30,10 @@ function updateBoxWithNewSt(box, myMembershipSt){ roleP.innerText = youAreXHere(myMembershipSt.myRoleHere); box.style.backgroundColor = roleToColor(myMembershipSt.myRoleHere); box.querySelector(".CL-my-chat-box-leave-btn").style.display = - (myMembershipSt.myRoleHere === userChatRoleDeleted ? "none" : "block"); + (shouldShowDeleteButton(myMembershipSt) ? "none" : "block"); } -function convertStToBox(myMembershipSt){ +function convertMyMembershipStToBox(myMembershipSt){ let chatURI = "/chat/" + myMembershipSt.chatNickname; let ID = myMembershipSt.chatId; @@ -62,13 +66,12 @@ function convertStToBox(myMembershipSt){ if (ev.button !== 0) return; chatRenunciationWinStoredId = ID; - activatePopupWindowById("chat-renunciation-win"); document.getElementById("chat-renunciation-win-title").innerText = "Do you really want to leave chat " + myMembershipSt.chatNickname + "?"; + activatePopupWindowById("chat-renunciation-win"); }; box.querySelector(".CL-my-chat-box-leave-btn").style.display = - (myMembershipSt.myRoleHere === userChatRoleDeleted ? "none" : "block"); - + (shouldShowDeleteButton(myMembershipSt) ? "none" : "block"); return box; } @@ -87,7 +90,7 @@ function updateLocalStateFromChatListUpdResp(chatListUpdResp){ if (myMembershipSt.myRoleHere === userChatRoleDeleted) continue; myChats.set(chatId, myMembershipSt); - let box = convertStToBox(myMembershipSt) + let box = convertMyMembershipStToBox(myMembershipSt) chatBoxes.set(chatId, box); literalChatList.appendChild(box); } diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_addmembertochat.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_addmembertochat.cpp index 017fb50..ce508aa 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_addmembertochat.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_addmembertochat.cpp @@ -3,16 +3,32 @@ #include namespace iu9cawebchat { - void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) { - assert(role != user_chat_role_deleted); + bool is_membership_row_present(SqliteConnection& conn, int64_t chatId, int64_t alienUserId) { + SqliteStatement req(conn, + "SELECT EXISTS(SELECT 1 FROM `user_chat_membership` WHERE `chatId` = ?1 AND `userId` = ?2)", + {{1, chatId}, {2, alienUserId}}, {}); + fsql_integer_or_null r{true, 0}; + int status = sqlite_stmt_step(req, {{0, &r}}, {}); + return (bool)r.value; + } + + void alter_user_chat_role(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) { int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId); int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId); - sqlite_nooutput(conn, - "INSERT INTO `user_chat_membership` (`userId`, `chatId`, `user_chatList_IncHistoryId`," - "`chat_IncHistoryId`, `role`) VALUES (?1, ?2, ?3, ?4, ?5)", - {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, - {4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {}); + if (!is_membership_row_present(conn, chatId, alienUserId)) { + sqlite_nooutput(conn, + "INSERT INTO `user_chat_membership` (`userId`, `chatId`, `user_chatList_IncHistoryId`," + "`chat_IncHistoryId`, `role`) VALUES (?1, ?2, ?3, ?4, ?5)", + {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, + {4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {}); + } else { + sqlite_nooutput(conn, + "UPDATE `user_chat_membership` SET `user_chatList_IncHistoryId` = ?3,`chat_IncHistoryId` = ?4," + "`role` = ?5 WHERE `userId` = ?1 AND `chatId` = ?2", + {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, + {4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {}); + } sqlite_nooutput(conn, "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1}, {2, chatId}}, {}); @@ -22,6 +38,12 @@ namespace iu9cawebchat { {{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {}); } + void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) { + assert(role != user_chat_role_deleted); + // int64_t old_role = get_role_of_user_in_chat(conn, alienUserId, chatId); + alter_user_chat_role(conn, chatId, alienUserId, role); + } + json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); @@ -33,12 +55,17 @@ namespace iu9cawebchat { try { alien = lookup_user_content_by_nickname(conn, alien_nickname); } catch (std::exception& e) { - return json::JSON(json::jdict{{"status", json::JSON(-1l)}}); + return at_api_error_gen_bad_recv(-1l); } + bool makeReadOnly = Sent["makeReadOnly"].toBool(); + int64_t aliens_old_role = get_role_of_user_in_chat(conn, alien.id, chatId); if (aliens_old_role == user_chat_role_deleted) { - make_her_a_member_of_the_midnight_crew(conn, chatId, alien.id, user_chat_role_regular); + make_her_a_member_of_the_midnight_crew(conn, chatId, alien.id, + makeReadOnly ? user_chat_role_read_only : user_chat_role_regular); + } else { + return at_api_error_gen_bad_recv(-2l); } json::JSON Recv; diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp index 794e64c..8ab1b94 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_createchat.cpp @@ -7,11 +7,11 @@ namespace iu9cawebchat { std::string new_chat_name = Sent["content"]["name"].asString(); std::string new_chat_nickname = Sent["content"]["nickname"].asString(); if (!check_nickname(new_chat_nickname) || !check_name(new_chat_name)) - return json::JSON(json::jdict{{"status", json::JSON(-1l)}}); + return at_api_error_gen_bad_recv(-1l); if (is_nickname_taken(conn, new_chat_nickname)) - return json::JSON(json::jdict{{"status", json::JSON(-2l)}}); + return at_api_error_gen_bad_recv(-2l); if (is_nickname_taken(conn, new_chat_nickname)) - return json::JSON(json::jdict{{"status", json::JSON(-3l)}}); + return at_api_error_gen_bad_recv(-3l); reserve_nickname(conn, new_chat_nickname); sqlite_nooutput(conn, diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_leavechat.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_leavechat.cpp index 7ed2be5..d2e8daa 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_leavechat.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_leavechat.cpp @@ -6,7 +6,7 @@ namespace iu9cawebchat { int64_t chatId = Sent["chatId"].asInteger().get_int(); if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) een9_THROW("Not a member"); - kick_from_chat(conn, uid, chatId); + kick_from_chat(conn, chatId, uid); json::JSON Recv; poll_update_chat_list(conn, uid, Sent, Recv); return Recv; diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_removememberfromchat.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_removememberfromchat.cpp index a2dc896..dbaca1e 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_removememberfromchat.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/api_removememberfromchat.cpp @@ -2,34 +2,17 @@ #include namespace iu9cawebchat { - void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId) { - if (get_role_of_user_in_chat(conn, alienUserId, chatId) == user_chat_role_deleted) - een9_THROW("Can't delete a deleted member"); - int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId); - int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId); - - sqlite_nooutput(conn, - "UPDATE `user_chat_membership` SET `user_chatList_IncHistoryId` = ?3," - "`chat_IncHistoryId` = ?4, `role` = ?5 WHERE `userId` = ?1 AND `chatId` = ?2", - {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, - {4, chat_HistoryId_BEFORE_EV + 1}, {5, user_chat_role_deleted}}, {}); - - sqlite_nooutput(conn, - "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1}, - {2, chatId}}, {}); - - sqlite_nooutput(conn, - "UPDATE `user` SET `chatList_HistoryId` = ?1 WHERE `id` = ?2", - {{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {}); + void kick_from_chat(SqliteConnection& conn, int64_t chatId, int64_t alienUserId) { + alter_user_chat_role(conn, chatId, alienUserId, user_chat_role_deleted); } json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); - if (my_role_here != user_chat_role_deleted) + if (my_role_here != user_chat_role_admin) een9_THROW("Only admin can delete members of chat"); - int64_t badAlienId = Sent["chatUpdReq"]["userId"].asInteger().get_int(); - kick_from_chat(conn, badAlienId, chatId); + int64_t badAlienId = Sent["userId"].asInteger().get_int(); + kick_from_chat(conn, chatId, badAlienId); json::JSON Recv; poll_update_chat(conn, Sent, Recv); return Recv; diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp index 363c61c..f2fa7dd 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp @@ -33,7 +33,6 @@ namespace iu9cawebchat { void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv) { Recv["status"].asInteger() = json::Integer(0l); // todo: in libjsonincpp: get rid of Integer - printf("%s\n", json::generate_str(Sent, json::print_pretty).c_str()); Recv["chatListUpdResp"] = poll_update_chat_list_resp(conn, userId, Sent["chatListUpdReq"]["LocalHistoryId"].asInteger().get_int()); } @@ -54,7 +53,7 @@ namespace iu9cawebchat { json::jarr messages; SqliteStatement messages_changes(conn, - "SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` " + "SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `message` " "WHERE `chatId` = ?1 AND ( `chat_IncHistoryId` > ?2 OR ( ?3 <= `id` AND `id` <= ?4 ) )", {{1, chatId}, {2, LocalHistoryId}, {3, QSEG_A}, {4, QSEG_B}}, {}); while (true) { @@ -139,7 +138,7 @@ namespace iu9cawebchat { int64_t QSEG_A, int64_t QSEG_B) { Recv["status"].asInteger() = json::Integer(0l); - Recv["charUpdResp"] = poll_update_chat_important_segment_resp(conn, + Recv["chatUpdResp"] = poll_update_chat_important_segment_resp(conn, Sent["chatUpdReq"]["chatId"].asInteger().get_int(), Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int(), QSEG_A, QSEG_B); } @@ -160,7 +159,6 @@ namespace iu9cawebchat { json::JSON internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { json::JSON Recv; poll_update_chat_list(conn, uid, Sent, Recv); - printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str()); return Recv; } diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp index eafd5af..384ed11 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp @@ -5,6 +5,10 @@ #include "../str_fields.h" namespace iu9cawebchat { + json::JSON at_api_error_gen_bad_recv(int64_t code) { + return json::JSON(json::jdict{{"status", json::JSON(code)}}); + } + const char* stringify_user_chat_role(int64_t role) { if (role == user_chat_role_admin) return "admin"; diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h index 6da7244..ba7a4f1 100644 --- a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h +++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h @@ -8,6 +8,8 @@ #include namespace iu9cawebchat { + json::JSON at_api_error_gen_bad_recv(int64_t code = -1); + constexpr int64_t user_chat_role_admin = 1; constexpr int64_t user_chat_role_regular = 2; constexpr int64_t user_chat_role_read_only = 3; @@ -61,8 +63,9 @@ namespace iu9cawebchat { json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg); void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv); + void alter_user_chat_role(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role); void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role); - void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId); + void kick_from_chat(SqliteConnection& conn, int64_t chatId, int64_t alienUserId); bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname); void reserve_nickname(SqliteConnection& conn, const std::string& nickname);