From ab1fb6ef88054ab7572b8c3eef97d1ddccc10f60 Mon Sep 17 00:00:00 2001 From: leca Date: Tue, 13 Aug 2024 18:21:54 +0300 Subject: [PATCH] add oportunity to change info --- src/db.js | 7 +- src/index.js | 214 ++++++++++++++++++++++-------------------- src/interactions.js | 219 ++++++++++++++++++++++++++++++++++--------- translations/en.json | 3 +- translations/ru.json | 3 +- 5 files changed, 295 insertions(+), 151 deletions(-) diff --git a/src/db.js b/src/db.js index 58db7e0..2c2409e 100644 --- a/src/db.js +++ b/src/db.js @@ -71,6 +71,10 @@ const eraseUserMessages = async (roomid) => { await db.query("DELETE FROM messages WHERE sender = $1 OR recipient = $1", [roomId]); } +const eraseUserPfp = async (roomId) => { + await db.query("DELETE FROM media WHERE owner = $1 AND purpose = 'p'", [roomId]); +} + const selectProfilesForUser = async (roomId) => { const { myrange, myinterest, mysex, myage, mycity } = (await db.query(`SELECT range AS myrange, interest AS myinterest, @@ -238,5 +242,6 @@ export { getCityNameByID, getCountryNameByID, doesUserExist, - createUser + createUser, + eraseUserPfp }; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 82d1a8c..746efb9 100644 --- a/src/index.js +++ b/src/index.js @@ -33,10 +33,25 @@ import { getUserLanguage, findCity, doesUserExist, - createUser + createUser, + eraseUserPfp, + getProfileInfo } from './db.js'; -import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js"; +import { + processRequest, + showRandomProfileToUser, + showNewLikes, + wait_start, + range, + interest, + pictures, + loc, + age, + sex, + showProfileToUser, + showProfile +} from "./interactions.js"; import { db } from "./db.js"; @@ -87,126 +102,76 @@ client.on("room.message", async (roomId, event) => { switch (current_action) { case "wait_start": - let a = answer.split(" "); - if (a[0] !== "!start") return; - if (a[1] !== "ru" && a[1] !== "en") return; - await setUserLanguage(roomId, a[1]); - i18n.locale = a[1]; - await setUserState(roomId, "location"); - await client.sendText(roomId, i18n.t(["setup", "location"])); - + await wait_start(client, roomId, current_action, answer, false); break; + case "wait_start_u": + await wait_start(client, roomId, current_action, answer, true); + break; + case "location": - let number = parseInt(answer); - if (!number) { - let cities = await findCity(answer); - await client.sendText(roomId, i18n.t(["setup", "choosecity"], { - number1: cities[0].id, - name1: cities[0].name, - country1: cities[0].country, - lat1: cities[0].lat, - lng1: cities[0].lng, - - number2: cities[1].id, - name2: cities[1].name, - country2: cities[1].country, - lat2: cities[1].lat, - lng2: cities[1].lng, - - number3: cities[2].id, - name3: cities[2].name, - country3: cities[2].country, - lat3: cities[2].lat, - lng3: cities[2].lng, - - number4: cities[3].id, - name4: cities[3].name, - country4: cities[3].country, - lat4: cities[3].lat, - lng4: cities[3].lng, - - number5: cities[4].id, - name5: cities[4].name, - country5: cities[4].country, - lat5: cities[4].lat, - lng5: cities[4].lng, - })); - } else { - await processRequest(client, roomId, current_action, number, 'range'); - } + await loc(client, roomId, current_action, answer, false); break; + case "location_u": + await loc(client, roomId, current_action, answer, true); + break; + case "range": - answer = parseInt(answer.split(" ")[0]); - if (!answer && answer != 0) { - await client.sendText(roomId, i18n.t(["setup", "range"])); - return; - } - await processRequest(client, roomId, current_action, answer, 'name'); + await range(client, roomId, current_action, answer, false); break; + case "range_u": + await range(client, roomId, current_action, answer, true); + break; + case "name": await processRequest(client, roomId, current_action, answer, 'age'); break; - case "age": - answer = parseInt(answer); - if (!answer) { - await client.sendText(roomId, i18n.t(["setup", "age"])); - return; - } + case "name_u": + await processRequest(client, roomId, current_action, answer, 'menu'); + break; - if (answer < 14) { - await client.sendText(roomId, i18n.t(["errors", "tooyoung"])); - await client.leaveRoom(roomId); - await fullEraseUser(roomId); - return; - } - await processRequest(client, roomId, current_action, answer, 'sex'); + case "age": + await age(client, roomId, current_action, answer, false); break; + case "age_u": + await age(client, roomId, current_action, answer, true); + break; + case "sex": - answer = answer.toLowerCase().trim(); - if (answer.toLowerCase() != "male" && answer.toLowerCase() != "female") { - await client.sendText(roomId, i18n.t(["errors", "twosexes"])); - return; - } - await processRequest(client, roomId, current_action, answer[0], 'interest'); + await sex(client, roomId, current_action, answer, false); break; + case "sex_u": + await sex(client, roomId, current_action, answer, true); + break; + case "interest": - answer = answer.toLowerCase().trim(); - if (answer != "male" && answer != "female" && answer != "both") { - await client.sendText(roomId, i18n.t(["errors", "didntunderstand"])); - return; - } - await processRequest(client, roomId, current_action, answer[0], 'description'); + await interest(client, roomId, current_action, answer, false); break; + case "interest_u": + await interest(client, roomId, current_action, answer, true); + break; + case "description": await processRequest(client, roomId, current_action, answer, 'pictures'); break; - case "pictures": - if (event.content?.msgtype !== 'm.image' && event.content?.msgtype !== 'm.video') { - await client.sendText(roomId, i18n.t(["setup", "done"])); - await setUserState(roomId, 'view_profiles'); - await showRandomProfileToUser(client, roomId); - } else { - let pictures_count = parseInt(await getAmountOfUserPictures(roomId)); - if (pictures_count >= maxAmountOfPhotoesPerUser) { - await client.sendText(roomId, i18n.t(["errors", "toomuch"])); - await setUserState(roomId, 'view_profiles'); - await showRandomProfileToUser(client, roomId); - } else { - let mxc = await uploadMediaFromEvent(client, event); - let type = convertMsgType(msgtype); - await appendUserPictures(roomId, mxc, type); - let pictures_count = await getAmountOfUserPictures(roomId); - if (pictures_count < maxAmountOfPhotoesPerUser) { - await client.sendText(roomId, i18n.t(["setup", "more"], { amount: String(maxAmountOfPhotoesPerUser - pictures_count) })); - } else { - await client.sendText(roomId, i18n.t(["setup", "enough"])); - await client.sendText(roomId, i18n.t(["setup", "done"])); - await setUserState(roomId, 'view_profiles'); - await showRandomProfileToUser(client, roomId); - } - } - } + case "description_u": + await processRequest(client, roomId, current_action, answer, 'menu'); break; + + case "pictures": + pictures(client, roomId, current_action, event, false); + break; + case "pictures_u": + pictures(client, roomId, current_action, event, true); + break; + + case "language_u": + if (answer !== "ru" && answer !== "en") return; + await setUserLanguage(roomId, answer); + i18n.locale = answer; + await setUserState(roomId, "menu"); + await client.sendText(roomId, i18n.t(["general", "menu"])); + break; + case "view_profiles": if (answer == 'πŸ‘οΈ' || answer == '❀️' || answer == '1') { let currently_viewing = await getUserCurrentlyViewingProfile(roomId); @@ -248,6 +213,11 @@ client.on("room.message", async (roomId, event) => { break; case 'menu': switch (answer) { + case '0': + let profile = await getProfileInfo(roomId); + await showProfile(client, roomId, profile); + await client.sendText(roomId, i18n.t(["general", "menu"])); + break; case '1': await setUserState(roomId, 'view_profiles'); await showRandomProfileToUser(client, roomId); @@ -288,6 +258,42 @@ client.on("room.message", async (roomId, event) => { } await client.sendText(roomId, i18n.t(["general", "menu"])); break; + case '4': + setUserState(roomId, 'location_u'); + await client.sendText(roomId, i18n.t(["setup", "location"])); + break; + case '5': + setUserState(roomId, 'range_u'); + await client.sendText(roomId, i18n.t(["setup", "range"])); + break; + case '6': + setUserState(roomId, 'name_u'); + await client.sendText(roomId, i18n.t(["setup", "name"])); + break; + case '7': + setUserState(roomId, 'age_u'); + await client.sendText(roomId, i18n.t(["setup", "age"])); + break; + case '8': + setUserState(roomId, 'sex_u'); + await client.sendText(roomId, i18n.t(["setup", "sex"])); + break; + case '9': + setUserState(roomId, 'interest_u'); + await client.sendText(roomId, i18n.t(["setup", "interest"])); + break; + case '10': + setUserState(roomId, 'description_u'); + await client.sendText(roomId, i18n.t(["setup", "description"])); + break; + case '11': + await eraseUserPfp(roomId); + setUserState(roomId, 'pictures_u'); + await client.sendText(roomId, i18n.t(["setup", "pictures"])); + break; + case '12': + setUserState(roomId, 'language_u'); + await client.sendText(roomId, i18n.t(["setup", "language"])); default: await client.sendText(roomId, i18n.t(["errors", "didntunderstand"])); await client.sendText(roomId, i18n.t(["general", "menu"])); diff --git a/src/interactions.js b/src/interactions.js index 383eb7f..5cb99d4 100644 --- a/src/interactions.js +++ b/src/interactions.js @@ -2,24 +2,34 @@ import { logError, logInfo, readConfig, + uploadMediaFromEvent, + convertMsgType } from './utils.js'; import { + appendUserPictures, + checkForMutualLike, + fullEraseUser, + getAmountOfUserPictures, + setUserLanguage, + getUserLanguage, + findCity, db, setUserState, selectProfilesForUser, setUserCurrentlyViewingProfile, getProfileInfo, getAllLikesForUser, - checkForMutualLike, markLikeAsRead, - getUserLanguage, getCountryNameByID, getCityNameByID } from "./db.js"; import fs from 'fs'; +const config = readConfig(); +const maxAmountOfPhotoesPerUser = config.maxAmountOfPhotoesPerUser; + import { I18n } from "i18n-js"; const i18n = new I18n({ @@ -45,26 +55,21 @@ const processRequest = async (client, roomId, question, answer, nextQuestion) => await client.sendText(roomId, i18n.t(["errors", "toobig"])); return; } - await db.query(`UPDATE users SET ${question} = $1 WHERE room_id = $2`, [answer, roomId]); + let q = question.split("_") + if (q[1] == "u") + await db.query(`UPDATE users SET ${q[0]} = $1 WHERE room_id = $2`, [answer, roomId]); + else + await db.query(`UPDATE users SET ${question} = $1 WHERE room_id = $2`, [answer, roomId]); await client.sendText(roomId, i18n.t(["general", "setopt"], { opt: answer })); - - await client.sendText(roomId, i18n.t(["setup", nextQuestion])); + if (nextQuestion == "menu") + await client.sendText(roomId, i18n.t(["general", "menu"])); + else + await client.sendText(roomId, i18n.t(["setup", nextQuestion])); setUserState(roomId, nextQuestion); }; -const showRandomProfileToUser = async (client, roomId) => { - let preferredLanguage = await getUserLanguage(roomId); - if (!preferredLanguage) preferredLanguage = i18n.defaultLocale; - i18n.locale = preferredLanguage; - - let chosenProfile = await selectProfilesForUser(roomId); - - if (!chosenProfile) { - await client.sendText(roomId, i18n.t(["errors", "noprofiles"])); - return; - } - +const showProfile = async (client, roomId, chosenProfile) => { chosenProfile.country = await getCountryNameByID(chosenProfile.location); chosenProfile.city = await getCityNameByID(chosenProfile.location); @@ -73,7 +78,6 @@ const showRandomProfileToUser = async (client, roomId) => { ${chosenProfile.name}, ${chosenProfile.sex == 'm' ? 'male' : 'female'}, ${chosenProfile.age}. ${chosenProfile.description}`; - await setUserCurrentlyViewingProfile(roomId, chosenProfile.room_id); await client.sendText(roomId, message); if (chosenProfile.media) { @@ -91,7 +95,23 @@ ${chosenProfile.description}`; }); } } +} +const showRandomProfileToUser = async (client, roomId) => { + let preferredLanguage = await getUserLanguage(roomId); + if (!preferredLanguage) preferredLanguage = i18n.defaultLocale; + i18n.locale = preferredLanguage; + + let chosenProfile = await selectProfilesForUser(roomId); + + if (!chosenProfile) { + await client.sendText(roomId, i18n.t(["errors", "noprofiles"])); + return; + } + + await showProfile(client, roomId, chosenProfile); + + await setUserCurrentlyViewingProfile(roomId, chosenProfile.room_id); await client.sendText(roomId, i18n.t(["general", "rate"])); }; @@ -102,35 +122,11 @@ const showProfileToUser = async (client, roomId, profileId) => { let profileInfo = await getProfileInfo(profileId); - profileInfo.country = await getCountryNameByID(profileInfo.location); - profileInfo.city = await getCityNameByID(profileInfo.location); - - let message = - `${profileInfo.country}, ${profileInfo.city}. -${profileInfo.name}, ${profileInfo.sex == 'm' ? 'male' : 'female'}, ${profileInfo.age}. -${profileInfo.description}`; - await client.sendText(roomId, i18n.t(["general", "showalike"])); - await client.sendText(roomId, message); + await showProfile(client, roomId, profileInfo); - if (profileInfo.media) { - for (let media of profileInfo.media) { - let msgtype; - if (media.type == 'p') { - msgtype = "m.image"; - } else if (media.type == 'v') { - msgtype = "m.video"; - } - await client.sendMessage(roomId, { - msgtype: msgtype, - body: "Profile media", - url: media.url - }); - } - } await client.sendText(roomId, i18n.t(["general", "mxid"], { mxid: profileInfo.mx_id })); - }; const showNewLikes = async (client, roomId) => { @@ -143,4 +139,139 @@ const showNewLikes = async (client, roomId) => { } }; -export { processRequest, showRandomProfileToUser, showNewLikes, showProfileToUser }; \ No newline at end of file +const wait_start = async (client, roomId, current_action, answer, update) => { + let a = answer.split(" "); + if (a[0] !== "!start") return; + if (a[1] !== "ru" && a[1] !== "en") return; + await setUserLanguage(roomId, a[1]); + i18n.locale = a[1]; + await setUserState(roomId, update ? "menu" : "location"); + await client.sendText(roomId, i18n.t(["setup", "location"])); +} + +const loc = async (client, roomId, current_action, answer, update) => { + let number = parseInt(answer); + if (!number) { + let cities = await findCity(answer); + await client.sendText(roomId, i18n.t(["setup", "choosecity"], { + number1: cities[0].id, + name1: cities[0].name, + country1: cities[0].country, + lat1: cities[0].lat, + lng1: cities[0].lng, + + number2: cities[1].id, + name2: cities[1].name, + country2: cities[1].country, + lat2: cities[1].lat, + lng2: cities[1].lng, + + number3: cities[2].id, + name3: cities[2].name, + country3: cities[2].country, + lat3: cities[2].lat, + lng3: cities[2].lng, + + number4: cities[3].id, + name4: cities[3].name, + country4: cities[3].country, + lat4: cities[3].lat, + lng4: cities[3].lng, + + number5: cities[4].id, + name5: cities[4].name, + country5: cities[4].country, + lat5: cities[4].lat, + lng5: cities[4].lng, + })); + } else { + await processRequest(client, roomId, current_action, number, update ? "menu" : "range"); + } +} + +const range = async (client, roomId, current_action, answer, update) => { + answer = parseInt(answer.split(" ")[0]); + if (!answer && answer != 0) { + await client.sendText(roomId, i18n.t(["setup", "range"])); + return; + } + await processRequest(client, roomId, current_action, answer, update ? "menu" : "name"); +} + +const age = async (client, roomId, current_action, answer, update) => { + answer = parseInt(answer); + if (!answer) { + await client.sendText(roomId, i18n.t(["setup", "age"])); + return; + } + + if (answer < 14) { + await client.sendText(roomId, i18n.t(["errors", "tooyoung"])); + await client.leaveRoom(roomId); + await fullEraseUser(roomId); + return; + } + await processRequest(client, roomId, current_action, answer, update ? "menu" : "sex"); +} + +const sex = async (client, roomId, current_action, answer, update) => { + answer = answer.toLowerCase().trim(); + if (answer.toLowerCase() != "male" && answer.toLowerCase() != "female") { + await client.sendText(roomId, i18n.t(["errors", "twosexes"])); + return; + } + await processRequest(client, roomId, current_action, answer[0], update ? "menu" : "interest"); +} + +const interest = async (client, roomId, current_action, answer, update) => { + answer = answer.toLowerCase().trim(); + if (answer != "male" && answer != "female" && answer != "both") { + await client.sendText(roomId, i18n.t(["errors", "didntunderstand"])); + return; + } + await processRequest(client, roomId, current_action, answer[0], update ? "menu" : "description"); +} + +const pictures = async (client, roomId, current_action, event, update) => { + const msgtype = event.content.msgtype; + if (event.content?.msgtype !== 'm.image' && event.content?.msgtype !== 'm.video') { + await client.sendText(roomId, i18n.t(["setup", "done"])); + await setUserState(roomId, update ? "menu" : "view_profiles"); + await showRandomProfileToUser(client, roomId); + } else { + let pictures_count = parseInt(await getAmountOfUserPictures(roomId)); + if (pictures_count >= maxAmountOfPhotoesPerUser) { + await client.sendText(roomId, i18n.t(["errors", "toomuch"])); + await setUserState(roomId, update ? "menu" : "view_profiles"); + await showRandomProfileToUser(client, roomId); + } else { + let mxc = await uploadMediaFromEvent(client, event); + let type = convertMsgType(msgtype); + await appendUserPictures(roomId, mxc, type); + let pictures_count = await getAmountOfUserPictures(roomId); + if (pictures_count < maxAmountOfPhotoesPerUser) { + await client.sendText(roomId, i18n.t(["setup", "more"], { amount: String(maxAmountOfPhotoesPerUser - pictures_count) })); + } else { + await client.sendText(roomId, i18n.t(["setup", "enough"])); + await client.sendText(roomId, i18n.t(["setup", "done"])); + await setUserState(roomId, update ? "menu" : "view_profiles"); + await showRandomProfileToUser(client, roomId); + } + } + } +} + +export { + processRequest, + showRandomProfileToUser, + showNewLikes, + showProfileToUser, + wait_start, + loc, + range, + age, + sex, + interest, + pictures, + showProfile +}; \ No newline at end of file diff --git a/translations/en.json b/translations/en.json index 73f60ef..1a28b9b 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2,7 +2,7 @@ "general": { "welcome": "Hello! I am a bot for new meetings in Matrix!\nTo start, please, type \"!start \", where language are 'en' or 'ru'.\nI'll ask some questions to fill your profile. You can change that info later, if you will.\nThen, I'll start showing you another people's profiles.\nYou can like other profile, skip or send a message to the person.\nIf you mutually liked each other, you'll be given each other's MXID to start a DM.\nMy source code can be found on gitea: https://git.foxarmy.org/leca/heart2heart.\n\nGood luck!", "rate": "Decide wether you like (πŸ‘οΈ, ❀️ or 1), dislike (πŸ‘ŽοΈ, πŸ’” or 2) or wanna send a messsage (πŸ’Œ or 3). Write '🏠️' or 4 to quit to menu. Any other response counts as dislike.", - "menu": "Pick an action:\n1) Start watching profiles\n2) Check for mutual likes\n3) Check for messages", + "menu": "Pick an action:\n0) Show your profile\n1) Start watching profiles\n2) Check for mutual likes\n3) Check for messages\n4) Change location\n5) Change range\n6) Change name\n7) Change age\n8) Change sex\n9) Change interest\n10) Change description\n11) Change pictures\n12) Change langage", "newlikes": "You have new mutual like(s)! Go into menu (🏠️) to see!", "showalike": "This person and you mutually liked each other!", "mxid": "Go drop them a line! Their MXID is: %{mxid}", @@ -15,6 +15,7 @@ "setopt": "Got \"%{opt}\"" }, "setup": { + "language": "Write a locale name, for example \"ru\" or \"en\".", "location": "Write a name of city you are living in. Write them in English. For example, 'Moscow', 'Washington', or 'Kyiv'.", "choosecity": "These are 5 most similar names of cities. Pick a city by writing its number. If you are unsatisfied with results, write a name of your city in other way.\n%{number1}) %{name1}, %{country1}. (%{lat1}, %{lng1})\n%{number2}) %{name2}, %{country2}. (%{lat2}, %{lng2})\n%{number3}) %{name3}, %{country3}. (%{lat3}, %{lng3})\n%{number4}) %{name4}, %{country4}. (%{lat4}, %{lng4})\n%{number5}) %{name5}, %{country5}. (%{lat5}, %{lng5})", "range": "Write a radius (in kilometers) of a circle you'd like to find people in (Write 0 if you don't care or small values to find people in your city only)", diff --git a/translations/ru.json b/translations/ru.json index 219c6c8..69cb9ce 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -2,7 +2,7 @@ "general": { "welcome": "ΠŸΡ€ΠΈΠ²Π΅Ρ‚! Π― Π±ΠΎΡ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π²Π°ΠΌ Π½Π°ΠΉΡ‚ΠΈ Π½ΠΎΠ²Ρ‹Π΅ знакомства Π² ΠœΠ°Ρ‚Ρ€ΠΈΠΊΡΠ΅!\nΠ§Ρ‚ΠΎΠ±Ρ‹ Π½Π°Ρ‡Π°Ρ‚ΡŒ, Π½Π°ΠΏΠΈΡˆΠΈΡ‚Π΅ \"!start <язык>\". ВмСсто <язык> Π²ΡΡ‚Π°Π²ΡŒΡ‚Π΅ ru ΠΈΠ»ΠΈ en.\nΠ― ΡΠΏΡ€ΠΎΡˆΡƒ нСсколько вопросов, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ваш ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ. Π’Ρ‹ смоТСтС ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΏΠΎΠ·ΠΆΠ΅.\nПослС этого, я Π½Π°Ρ‡Π½Ρƒ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ Π²Π°ΠΌ ΠΏΡ€ΠΎΡ„ΠΈΠ»ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ..\nΠ’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π»Π°ΠΉΠΊΠ°Ρ‚ΡŒ, ΡΠΊΠΈΠΏΠ°Ρ‚ΡŒ Ρ‡ΡƒΠΆΠΈΠ΅ ΠΏΡ€ΠΎΡ„ΠΈΠ»ΠΈ ΠΈΠ»ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΈΠΌ сообщСния.\nЕсли Π²Ρ‹ Π²Π·Π°ΠΈΠΌΠ½ΠΎ Π»Π°ΠΉΠΊΠ½ΡƒΠ»ΠΈ Π΄Ρ€ΡƒΠ³ Π΄Ρ€ΡƒΠ³, Π²Π°ΠΌ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠΊΠ°Π·Π°Π½Ρ‹ ваши MXID.\nМой исходный ΠΊΠΎΠ΄ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡ‚ΠΈ Π½Π° gitea: https://git.foxarmy.org/leca/heart2heart.\n\nΠ£Π΄Π°Ρ‡ΠΈ!", "rate": "Π Π΅ΡˆΠΈΡ‚Π΅, нравится Π»ΠΈ (πŸ‘οΈ, ❀️ or 1), Π½Π΅ нравится (πŸ‘ŽοΈ, πŸ’” or 2) ΠΈΠ»ΠΈ Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ сообщСниС (πŸ’Œ or 3). ΠΠ°ΠΏΠΈΡˆΠΈΡ‚Π΅ '🏠️' ΠΈΠ»ΠΈ 4 Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹ΠΉΡ‚ΠΈ Π² мСню. Π›ΡŽΠ±ΠΎΠΉ Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΎΡ‚Π²Π΅Ρ‚ считаСтся ΠΊΠ°ΠΊ \"Π½Π΅ нравится\".", - "menu": "Π’Ρ‹Π±Π΅Ρ€Π΅Ρ‚Π΅ дСйствиС:\n1) ΠΠ°Ρ‡Π°Ρ‚ΡŒ просмотр ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΉ\n2) ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π²Π·Π°ΠΈΠΌΠ½Ρ‹Π΅ Π»Π°ΠΉΠΊΠΈ\n3) ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ сообщСния", + "menu": "Π’Ρ‹Π±Π΅Ρ€Π΅Ρ‚Π΅ дСйствиС:\n0) ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ваш ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ\n1) ΠΠ°Ρ‡Π°Ρ‚ΡŒ просмотр ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΉ\n2) ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π²Π·Π°ΠΈΠΌΠ½Ρ‹Π΅ Π»Π°ΠΉΠΊΠΈ\n3) ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ сообщСния\n4) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ мСстополоТСниС\n5) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ радиус\n6) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ имя\n7) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ возраст\n8) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΠΎΠ»\n9) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ интСрСс\n10) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ описаниС\n11) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ профиля\n12) Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ язык", "newlikes": "Π£ вас Π½ΠΎΠ²Ρ‹ΠΉ(-ΠΈΠ΅) Π²Π·Π°ΠΈΠΌΠ½Ρ‹Π΅ Π»Π°ΠΉΠΊ(ΠΈ)! Π’Ρ‹ΠΉΠ΄ΠΈΡ‚Π΅ Π² мСню (🏠️) Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ!", "showalike": "Π­Ρ‚ΠΎΡ‚ Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊ ΠΈ Π²Ρ‹ Π΄Ρ€ΡƒΠ³ Π΄Ρ€ΡƒΠ³Π° Π²Π·Π°ΠΈΠΌΠ½ΠΎ Π»Π°ΠΉΠΊΠ½ΡƒΠ»ΠΈ!", "mxid": "Π˜Π΄ΠΈΡ‚Π΅ ΠΈ Π½Π°ΠΏΠΈΡˆΠΈΡ‚Π΅ этому Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊΡƒ! MXID: %{mxid}", @@ -15,6 +15,7 @@ "setopt": "Записал \"%{opt}\"" }, "setup": { + "language": "ΠΠ°ΠΏΠΈΡˆΠΈΡ‚Π΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π»ΠΎΠΊΠ°Π»ΠΈ. Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ \"ru\" ΠΈΠ»ΠΈ \"en\".", "location": "ΠΠ°ΠΏΠΈΡˆΠΈΡ‚Π΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π³ΠΎΡ€ΠΎΠ΄Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π²Ρ‹ ΠΆΠΈΠ²Ρ‘Ρ‚Π΅. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ английский язык. Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 'Moscow', 'Washington', or 'Kyiv'.", "choosecity": "Π’ΠΎΡ‚ 5 Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ ΠΏΠΎΡ…ΠΎΠΆΠΈΡ… ΠΏΠΎ названию Π³ΠΎΡ€ΠΎΠ΄ΠΎΠ². Π§Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ Π³ΠΎΡ€ΠΎΠ΄, Π½Π°ΠΏΠΈΡˆΠΈΡ‚Π΅ Π΅Π³ΠΎ Π½ΠΎΠΌΠ΅Ρ€. Если Π²Ρ‹ Π½Π΅ ΡƒΠ΄ΠΎΠ²Π»Π΅Ρ‚Π²ΠΎΡ€Π΅Π½Ρ‹ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°ΠΌΠΈ, Π½Π°ΠΏΠΈΡˆΠΈΡ‚Π΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π³ΠΎΡ€ΠΎΠ΄Π° ΠΈΠ½Π°Ρ‡Π΅.\n%{number1}) %{name1}, %{country1}. (%{lat1}, %{lng1})\n%{number2}) %{name2}, %{country2}. (%{lat2}, %{lng2})\n%{number3}) %{name3}, %{country3}. (%{lat3}, %{lng3})\n%{number4}) %{name4}, %{country4}. (%{lat4}, %{lng4})\n%{number5}) %{name5}, %{country5}. (%{lat5}, %{lng5})", "range": "ΠΠ°ΠΏΠΈΡˆΠΈΡ‚Π΅ радиус (Π² ΠΊΠΈΠ»ΠΎΠΌΠ΅Ρ‚Ρ€Π²Ρ…), Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π²Ρ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ Π±Ρ‹ Π½Π°ΠΉΡ‚ΠΈ людСй (ΠΠ°ΠΏΠΈΡˆΠΈΡ‚Π΅ 0, Ссли Π²Π°ΠΌ Π½Π΅ Π²Π°ΠΆΠ΅Π½ радиус ΠΈΠ»ΠΈ малСнькиС значСния, Ссли Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ Π½Π°ΠΉΡ‚ΠΈ людСй Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² своём Π³ΠΎΡ€ΠΎΠ΄Π΅)",