translations and remade location

This commit is contained in:
leca 2024-08-12 17:32:37 +03:00
parent 96d7cfb7de
commit 9c0a9db195
6 changed files with 148 additions and 111 deletions

View File

@ -7,8 +7,7 @@ CREATE TABLE IF NOT EXISTS users (
interest CHAR, -- male, female or both ('m', 'f' and 'b') interest CHAR, -- male, female or both ('m', 'f' and 'b')
description VARCHAR(512) DEFAULT NULL, description VARCHAR(512) DEFAULT NULL,
language VARCHAR(8) DEFAULT 'en', language VARCHAR(8) DEFAULT 'en',
country VARCHAR(64) DEFAULT NULL, location INTEGER DEFAULT NULL, -- link to ID, cities
city VARCHAR(64) DEFAULT NULL,
range DOUBLE PRECISION DEFAULT 20.0, range DOUBLE PRECISION DEFAULT 20.0,
current_action VARCHAR(16) DEFAULT NULL, current_action VARCHAR(16) DEFAULT NULL,
currently_viewing VARCHAR(64) --link to "room_id" currently_viewing VARCHAR(64) --link to "room_id"
@ -40,6 +39,7 @@ CREATE TABLE IF NOT EXISTS messages (
CREATE UNIQUE INDEX IF NOT EXISTS unique_messages ON messages(sender, recipient); CREATE UNIQUE INDEX IF NOT EXISTS unique_messages ON messages(sender, recipient);
CREATE TABLE IF NOT EXISTS cities ( CREATE TABLE IF NOT EXISTS cities (
ID SERIAL,
name VARCHAR(64), name VARCHAR(64),
lat REAL, lat REAL,
lng REAL, lng REAL,

View File

@ -48,6 +48,13 @@ const appendUserPictures = async (roomId, mxc, type) => {
await db.query("INSERT INTO media (owner, type, purpose, url) VALUES ($1, $2, 'p', $3)", [roomId, type, mxc]); await db.query("INSERT INTO media (owner, type, purpose, url) VALUES ($1, $2, 'p', $3)", [roomId, type, mxc]);
}; };
const fullEraseUser = async (roomId) => {
await eraseUser(roomId)
await eraseUserLikes(roomId);
await eraseUserMedia(roomId);
await eraseUserMessages(roomId);
}
const eraseUser = async (roomId) => { const eraseUser = async (roomId) => {
await db.query("DELETE FROM users WHERE room_id = $1", [roomId]); await db.query("DELETE FROM users WHERE room_id = $1", [roomId]);
}; };
@ -60,16 +67,19 @@ const eraseUserMedia = async (roomId) => {
await db.query("DELETE FROM media WHERE owner = $1", [roomId]); await db.query("DELETE FROM media WHERE owner = $1", [roomId]);
}; };
const eraseUserMessages = async (roomid) => {
await db.query("DELETE FROM messages WHERE sender = $1 OR recipient = $1", [roomId]);
}
const selectProfilesForUser = async (roomId) => { const selectProfilesForUser = async (roomId) => {
const { myrange, myinterest, mysex, myage, mycity, mycountry } = (await db.query(`SELECT range AS myrange, const { myrange, myinterest, mysex, myage, mycity } = (await db.query(`SELECT range AS myrange,
interest AS myinterest, interest AS myinterest,
sex AS mysex, sex AS mysex,
age AS myage, age AS myage,
city AS mycity, location AS mycity
country AS mycountry
FROM users WHERE room_id = $1`, [roomId]) FROM users WHERE room_id = $1`, [roomId])
).rows[0]; ).rows[0];
const { lat, lng } = (await db.query("SELECT lat, lng FROM cities WHERE name = $1 AND country = $2", [mycity, mycountry])).rows[0]; const { lat, lng } = (await db.query("SELECT lat, lng FROM cities WHERE ID = $1", [mycity])).rows[0];
//Selecting profiles other than user's and fitting their needs //Selecting profiles other than user's and fitting their needs
/* /*
2 * ASIN(SQRT( 2 * ASIN(SQRT(
@ -80,26 +90,23 @@ const selectProfilesForUser = async (roomId) => {
)) ))
* 6371 <= $5::double precision * 6371 <= $5::double precision
*/ */
// AND (range >= $5::double precision AND range != 0) //
let user = (await db.query(`SELECT let user = (await db.query(`SELECT
room_id, name, age, sex, description, country, city FROM users room_id, name, age, sex, description, location, range FROM users
WHERE WHERE
age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)]) age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
AND room_id != $2 AND room_id != $2
AND ${myinterest !== 'b' ? "sex = $3" : "$3 = $3 AND $4 = $4 AND $5 = $5 AND $6 = $6 AND $7 = $7"} AND ${myinterest !== 'b' ? "sex = $3" : "$3 = $3 AND $4 = $4 AND $5 = $5 AND $6 = $6 AND $7 = $7"}
AND ${myrange !== 0 ? AND ${myrange !== 0 ?
`city = ANY(ARRAY(SELECT name `location = ANY(ARRAY(SELECT ID
FROM cities
WHERE name = ANY(ARRAY(SELECT name
FROM cities FROM cities
WHERE WHERE
check_distance($6::double precision, $7::double precision, lat, lng, $5::double precision) check_distance($6::double precision, $7::double precision, lat, lng, $5::double precision)
AND (check_distance($6::double precision, $7::double precision, lat, lng, range) OR range = 0)
))
))` ))`
: :
`check_distance($6::double precision, $7::double precision, (SELECT lat FROM cities WHERE name = city AND cities.country = country), (SELECT lng FROM cities WHERE name = city AND cities.country = country), range) `range = 0 OR
OR range = 0` check_distance($6::double precision, $7::double precision, (SELECT lat FROM cities WHERE ID = location), (SELECT lng FROM cities WHERE ID = location), range)`
} }
AND (interest = $4 OR interest = 'b') AND (interest = $4 OR interest = 'b')
ORDER BY RANDOM() ORDER BY RANDOM()
@ -148,7 +155,7 @@ const checkForMutualLike = async (roomId1, roomId2) => {
}; };
const getProfileInfo = async (roomId) => { const getProfileInfo = async (roomId) => {
let user = (await db.query("SELECT mx_id, room_id, name, age, sex, description, country, city FROM users WHERE room_id = $1", [roomId])).rows[0]; let user = (await db.query("SELECT mx_id, room_id, name, age, sex, description, location FROM users WHERE room_id = $1", [roomId])).rows[0];
if (!user) return null; if (!user) return null;
let media = await getUserProfilePictures(user.room_id); let media = await getUserProfilePictures(user.room_id);
user.media = media; user.media = media;
@ -192,45 +199,42 @@ const getUserLanguage = async (roomId) => {
return (await db.query("SELECT language FROM users WHERE room_id = $1", [roomId])).rows[0].language; return (await db.query("SELECT language FROM users WHERE room_id = $1", [roomId])).rows[0].language;
} }
const checkCountry = async (name) => { // const checkCountry = async (name) => {
let res = (await db.query(`SELECT country AS name, levenshtein(country, $1) AS similarity // let res = (await db.query(`SELECT country AS name, levenshtein(country, $1) AS similarity
FROM cities // FROM cities
ORDER BY similarity ASC // ORDER BY similarity ASC
LIMIT 3`, [name]) // LIMIT 3`, [name])
).rows; // ).rows;
if (res[0].similarity == 0) { // 'similarity' is actually inversed. The less 'similarity', the more it similar. 0 means the same // if (res[0].similarity == 0) { // 'similarity' is actually inversed. The less 'similarity', the more it similar. 0 means the same
return { // return {
exists: true // exists: true
}; // };
} // }
return { // return {
exists: false, // exists: false,
variants: res // variants: res
}; // };
} // }
const checkCity = async (name) => { const findCity = async (name) => {
let res = (await db.query(`SELECT name, country, levenshtein(name, $1) AS similarity return (await db.query(`SELECT ID, name, lat, lng, country, levenshtein(name, $1) AS similarity
FROM cities FROM cities
ORDER BY similarity ASC ORDER BY similarity ASC
LIMIT 5`, [name]) LIMIT 5`, [name])
).rows; ).rows;
}
if (res[0].similarity == 0) { const getCityNameByID = async (id) => {
return { return (await db.query(`SELECT name FROM cities WHERE ID = $1`, [id])).rows[0].name;
exists: true, }
country: res[0].country
} const getCountryNameByID = async (id) => {
} return (await db.query(`SELECT country FROM cities WHERE ID = $1`, [id])).rows[0].country;
return {
exists: false,
variants: res
}
} }
export { export {
eraseUser, fullEraseUser,
appendUserPictures, appendUserPictures,
setUserState, setUserState,
getCurrentUserAction, getCurrentUserAction,
@ -244,14 +248,13 @@ export {
getProfileInfo, getProfileInfo,
getAllLikesForUser, getAllLikesForUser,
markLikeAsRead, markLikeAsRead,
eraseUserLikes,
eraseUserMedia,
uploadMediaAsMessage, uploadMediaAsMessage,
insertMessageIntoDB, insertMessageIntoDB,
getUnreadMessages, getUnreadMessages,
markMessageAsRead, markMessageAsRead,
setUserLanguage, setUserLanguage,
getUserLanguage, getUserLanguage,
checkCountry, findCity,
checkCity getCityNameByID,
getCountryNameByID
}; };

View File

@ -20,9 +20,7 @@ import {
appendUserLikes, appendUserLikes,
appendUserPictures, appendUserPictures,
checkForMutualLike, checkForMutualLike,
eraseUser, fullEraseUser,
eraseUserLikes,
eraseUserMedia,
getAmountOfUserPictures, getAmountOfUserPictures,
getCurrentUserAction, getCurrentUserAction,
getUnreadMessages, getUnreadMessages,
@ -33,8 +31,9 @@ import {
uploadMediaAsMessage, uploadMediaAsMessage,
setUserLanguage, setUserLanguage,
getUserLanguage, getUserLanguage,
checkCountry, findCity,
checkCity getCityNameByID,
getCountryNameByID
} from './db.js'; } from './db.js';
import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js"; import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js";
@ -81,42 +80,66 @@ client.on("room.message", async (roomId, event) => {
if (a[1] !== "ru" && a[1] !== "en") return; if (a[1] !== "ru" && a[1] !== "en") return;
await setUserLanguage(roomId, a[1]); await setUserLanguage(roomId, a[1]);
i18n.locale = a[1] i18n.locale = a[1]
await setUserState(roomId, "country"); await setUserState(roomId, "location");
await client.sendText(roomId, i18n.t(["setup", "country"])); await client.sendText(roomId, i18n.t(["setup", "location"]));
break; break;
case "country": case "location":
let checkResultCountry = await checkCountry(answer); let number = parseInt(answer)
if (!checkResultCountry.exists) { if (!number) {
console.log(checkResultCountry.variants) let cities = await findCity(answer);
await client.sendText(roomId, i18n.t( await client.sendText(roomId, i18n.t(["setup", "choosecity"], {
["errors", "wrongcountry"], number1: cities[0].id,
{ name1: cities[0].name,
first: checkResultCountry.variants[0].name, country1: cities[0].country,
second: checkResultCountry.variants[1].name, lat1: cities[0].lat,
third: checkResultCountry.variants[2].name 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 {
// let cityName = await getCityNameByID(number);
// let countryName = await getCountryNameByID(number);
await processRequest(client, roomId, current_action, number, 'range');
} }
)); // let checkResultCity = await checkCity(answer);
return; // if (!checkResultCity.exists) {
} // await client.sendText(roomId, i18n.t(
await processRequest(client, roomId, current_action, answer, 'city'); // ["errors", "wrongcity"],
break; // {
case "city": // first: checkResultCity.variants[0].name,
let checkResultCity = await checkCity(answer); // second: checkResultCity.variants[1].name,
if (!checkResultCity.exists) { // third: checkResultCity.variants[2].name,
await client.sendText(roomId, i18n.t( // fourth: checkResultCity.variants[3].name,
["errors", "wrongcity"], // fifth: checkResultCity.variants[4].name,
{ // }
first: checkResultCity.variants[0].name, // ));
second: checkResultCity.variants[1].name, // return;
third: checkResultCity.variants[2].name, // }
fourth: checkResultCity.variants[3].name, // await processRequest(client, roomId, current_action, answer, 'range');
fifth: checkResultCity.variants[4].name,
}
));
return;
}
await processRequest(client, roomId, current_action, answer, 'range');
break; break;
case "range": case "range":
answer = parseInt(answer.split(" ")[0]); answer = parseInt(answer.split(" ")[0]);
@ -140,7 +163,7 @@ client.on("room.message", async (roomId, event) => {
await client.sendText(roomId, i18n.t(["errors", "tooyoung"])); await client.sendText(roomId, i18n.t(["errors", "tooyoung"]));
await client.leaveRoom(roomId); await client.leaveRoom(roomId);
await eraseUser(roomId); await fullEraseUser(roomId);
return; return;
} }
await processRequest(client, roomId, current_action, answer, 'sex'); await processRequest(client, roomId, current_action, answer, 'sex');
@ -204,7 +227,7 @@ client.on("room.message", async (roomId, event) => {
await appendUserLikes(roomId, currently_viewing); await appendUserLikes(roomId, currently_viewing);
let value = await checkForMutualLike(roomId, currently_viewing); let value = await checkForMutualLike(roomId, currently_viewing);
if (value) { if (value) {
await client.sendText(roomId, i18n.t(["general", "newlikes"])); await client.sendText(currently_viewing, i18n.t(["general", "newlikes"]));
await client.sendText(roomId, i18n.t(["general", "newlikes"])); await client.sendText(roomId, i18n.t(["general", "newlikes"]));
@ -238,7 +261,6 @@ client.on("room.message", async (roomId, event) => {
} else { } else {
await client.sendText(roomId, i18n.t(["errors", "alreadymessaged"])); await client.sendText(roomId, i18n.t(["errors", "alreadymessaged"]));
} }
await setUserState(roomId, 'view_profiles'); await setUserState(roomId, 'view_profiles');
await showRandomProfileToUser(client, roomId); await showRandomProfileToUser(client, roomId);
@ -306,13 +328,15 @@ client.on("room.message", async (roomId, event) => {
}); });
client.on("room.event", async (roomId, event) => { client.on("room.event", async (roomId, event) => {
try {
if (event.type === "m.room.member" && event.content?.membership === "leave") { if (event.type === "m.room.member" && event.content?.membership === "leave") {
await eraseUser(roomId); await fullEraseUser(roomId);
await eraseUserLikes(roomId);
await eraseUserMedia(roomId);
logInfo(`Bot has left a room with ID ${roomId}`) logInfo(`Bot has left a room with ID ${roomId}`)
client.leaveRoom(roomId); client.leaveRoom(roomId);
} }
} catch (e) {
logError(e);
}
}) })
client.on("room.invite", async (roomId, event) => { client.on("room.invite", async (roomId, event) => {

View File

@ -13,7 +13,9 @@ import {
getAllLikesForUser, getAllLikesForUser,
checkForMutualLike, checkForMutualLike,
markLikeAsRead, markLikeAsRead,
getUserLanguage getUserLanguage,
getCountryNameByID,
getCityNameByID
} from "./db.js"; } from "./db.js";
import fs from 'fs'; import fs from 'fs';
@ -58,11 +60,17 @@ const showRandomProfileToUser = async (client, roomId) => {
i18n.locale = preferredLanguage; i18n.locale = preferredLanguage;
let chosenProfile = await selectProfilesForUser(roomId); let chosenProfile = await selectProfilesForUser(roomId);
console.log(chosenProfile);
if (!chosenProfile) { if (!chosenProfile) {
await client.sendText(roomId, i18n.t(["errors", "noprofiles"])); await client.sendText(roomId, i18n.t(["errors", "noprofiles"]));
return; return;
} }
chosenProfile.country = await getCountryNameByID(chosenProfile.location);
chosenProfile.city = await getCityNameByID(chosenProfile.location);
let message = let message =
`${chosenProfile.country}, ${chosenProfile.city}. `${chosenProfile.country}, ${chosenProfile.city}.
${chosenProfile.name}, ${chosenProfile.sex == 'm' ? 'male' : 'female'}, ${chosenProfile.age}. ${chosenProfile.name}, ${chosenProfile.sex == 'm' ? 'male' : 'female'}, ${chosenProfile.age}.
@ -96,6 +104,10 @@ const showProfileToUser = async (client, roomId, profileId) => {
i18n.locale = preferredLanguage; i18n.locale = preferredLanguage;
let profileInfo = await getProfileInfo(profileId); let profileInfo = await getProfileInfo(profileId);
profileInfo.country = await getCountryNameByID(profileInfo.location);
profileInfo.city = await getCityNameByID(profileInfo.location);
let message = let message =
`${profileInfo.country}, ${profileInfo.city}. `${profileInfo.country}, ${profileInfo.city}.
${profileInfo.name}, ${profileInfo.sex == 'm' ? 'male' : 'female'}, ${profileInfo.age}. ${profileInfo.name}, ${profileInfo.sex == 'm' ? 'male' : 'female'}, ${profileInfo.age}.

View File

@ -8,15 +8,15 @@
"mxid": "Go drop them a line! Their MXID is: %{mxid}", "mxid": "Go drop them a line! Their MXID is: %{mxid}",
"message": "Write one message to this person or upload one picture/video to send them!", "message": "Write one message to this person or upload one picture/video to send them!",
"newmessage": "You've got new message(s)! Go to a menu (🏠️) to see!", "newmessage": "You've got new message(s)! Go to a menu (🏠️) to see!",
"showmessage": " sent you this: ", "showmessage": "%{user} sent you this:",
"nonewmessages": "You have no new messages!", "nonewmessages": "You have no new messages!",
"messagesent": "You message was sent!", "messagesent": "You message was sent!",
"msg": "Messages:", "msg": "Messages:",
"setopt": "Got \"%{opt}\"" "setopt": "Got \"%{opt}\""
}, },
"setup": { "setup": {
"country": "Write a name of country you are living in. Write them in English. For example, 'Russia', 'United States' or 'Ukraine'.", "location": "Write a name of city you are living in. Write them in English. For example, 'Moscow', 'Washington', or 'Kyiv'.",
"city": "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)", "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)",
"name": "Write a name of your profile. Not longer than 32 symbols", "name": "Write a name of your profile. Not longer than 32 symbols",
"description": "Write a description of your profile. Not longer than 512 symbols.", "description": "Write a description of your profile. Not longer than 512 symbols.",
@ -38,7 +38,6 @@
"notimplemented": "This feature is not yet implemented! Please, keep track of my updates at https://git.foxarmy.org/leca/heart2heart. Bother your admins to update, if released! ;)", "notimplemented": "This feature is not yet implemented! Please, keep track of my updates at https://git.foxarmy.org/leca/heart2heart. Bother your admins to update, if released! ;)",
"noprofiles": "There are no profiles left for you! I'm sorry. Please, come back later!", "noprofiles": "There are no profiles left for you! I'm sorry. Please, come back later!",
"alreadymessaged": "You have already messaged that person. Your message was not sent!", "alreadymessaged": "You have already messaged that person. Your message was not sent!",
"wrongcountry": "There is no such country. Perhaps, you meant %{first}, %{second} or %{third}?",
"wrongcity": "There is no such city. Perhaps, you meant %{first}, %{second}, %{third}, %{fourth} or %{fifth}?" "wrongcity": "There is no such city. Perhaps, you meant %{first}, %{second}, %{third}, %{fourth} or %{fifth}?"
} }
} }

View File

@ -15,8 +15,8 @@
"setopt": "Записал \"%{opt}\"" "setopt": "Записал \"%{opt}\""
}, },
"setup": { "setup": {
"country": "Напишите имя страны, в которой вы находитесь. Писать на английском языке. Например, 'Russia', 'United States' or 'Ukraine'.", "location": "Напишите название города, в котором вы живёте. Используйте английский язык. например, 'Moscow', 'Washington', or 'Kyiv'.",
"city": "Напишите имя города, в котором вы находитесь. Писать на английском языке. Например, '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, если вам не важен радиус или маленькие значения, если хотите найти людей только в своём городе)", "range": "Напишите радиус (в километрвх), в котором вы хотели бы найти людей (Напишите 0, если вам не важен радиус или маленькие значения, если хотите найти людей только в своём городе)",
"name": "Напишите имя вашего профиля. Не больше 32 символов", "name": "Напишите имя вашего профиля. Не больше 32 символов",
"description": "Напишите описание вашего профиля. Не больше 512 символов.", "description": "Напишите описание вашего профиля. Не больше 512 символов.",
@ -38,7 +38,6 @@
"notimplemented": "Эта возможность ещё не добавлена! Пожалуйста, следите за обновлениями на моём git'е: https://git.foxarmy.org/leca/heart2heart. Доставайте своих админов, чтобы обновлялись, когда новая версия выходит ;)", "notimplemented": "Эта возможность ещё не добавлена! Пожалуйста, следите за обновлениями на моём git'е: https://git.foxarmy.org/leca/heart2heart. Доставайте своих админов, чтобы обновлялись, когда новая версия выходит ;)",
"noprofiles": "Не могу найти профили, которые подходят вашим критериям! Извините. Попробуйте позже!", "noprofiles": "Не могу найти профили, которые подходят вашим критериям! Извините. Попробуйте позже!",
"alreadymessaged": "Вы уже отправляли сообщение этому человеку. Ваше сообщение не было отправлено!", "alreadymessaged": "Вы уже отправляли сообщение этому человеку. Ваше сообщение не было отправлено!",
"wrongcountry": "Не знаю такой страны. Возможно, вы имели в виду %{first}, %{second} или %{third}?",
"wrongcity": "Не знаю такого города. Возможно, вы имели в виду %{first}, %{second}, %{third}, %{fourth} или %{fifth}?" "wrongcity": "Не знаю такого города. Возможно, вы имели в виду %{first}, %{second}, %{third}, %{fourth} или %{fifth}?"
} }
} }