Added menu and core profile functionality

This commit is contained in:
leca 2024-08-05 21:21:43 +03:00
parent 01454892f8
commit d95fc59aac
10 changed files with 359 additions and 123 deletions

View File

@ -1,4 +1,5 @@
{
"homeserverURL": "https://matrix.org/",
"token": "Super secret token! Do not show to anyone! Even your mum! ;)"
"homeserverURL": "https://foxarmy.org/",
"token": "syt_bWVldHM_bwurBHyoTXGkyFHwwTbY_2IoMCg",
"maxAmountOfPhotoesPerUser": 5
}

View File

@ -1,11 +1,11 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" version="24.7.6">
<diagram id="R2lEEEUBdFMjLlhIrx00" name="Page-1">
<mxGraphModel dx="656" dy="384" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0" extFonts="Permanent Marker^https://fonts.googleapis.com/css?family=Permanent+Marker">
<mxGraphModel dx="1196" dy="534" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0" extFonts="Permanent Marker^https://fonts.googleapis.com/css?family=Permanent+Marker">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="7XvpWkOTBmjUhqAazzN1-1" value="users" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="285" y="130" width="140" height="410" as="geometry" />
<mxGeometry x="285" y="130" width="140" height="430" as="geometry" />
</mxCell>
<mxCell id="7XvpWkOTBmjUhqAazzN1-2" value="mx_id VARCHAR(64)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="30" width="140" height="30" as="geometry" />
@ -16,32 +16,32 @@
<mxCell id="7XvpWkOTBmjUhqAazzN1-4" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;pictures_urls TEXT[]&lt;/span&gt;&lt;br&gt;&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="90" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-9" value="name VARCHAR(32)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxCell id="M61CuUsqLGTxYLAPg2Ho-9" value="name VARCHAR(32)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="120" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-10" value="age SMALLINT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxCell id="M61CuUsqLGTxYLAPg2Ho-10" value="age SMALLINT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="150" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-18" value="gender BOOLEAN -- 1 means male" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxCell id="M61CuUsqLGTxYLAPg2Ho-18" value="sex CHAR, -- &#39;m&#39; of &#39;f&#39;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="180" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-17" value="interest VARCHAR(6) -- male, female or both." style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxGeometry y="210" width="140" height="30" as="geometry" />
<mxCell id="M61CuUsqLGTxYLAPg2Ho-17" value="interest CHAR, -- male, female or both (&#39;m&#39;, &#39;f&#39; and &#39;b&#39;)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="210" width="140" height="50" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-1" value="&lt;div&gt;description VARCHAR(512)&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxGeometry y="240" width="140" height="30" as="geometry" />
<mxCell id="M61CuUsqLGTxYLAPg2Ho-1" value="&lt;div&gt;description VARCHAR(512)&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="260" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-7" value="&lt;div&gt;country VARCHAR(64)&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxGeometry y="270" width="140" height="30" as="geometry" />
<mxCell id="M61CuUsqLGTxYLAPg2Ho-7" value="&lt;div&gt;country VARCHAR(64)&lt;/div&gt;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="290" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-8" value="city VARCHAR(64)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxGeometry y="300" width="140" height="30" as="geometry" />
<mxCell id="M61CuUsqLGTxYLAPg2Ho-8" value="city VARCHAR(64)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="320" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-11" value="current_action VARCHAR(16)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxGeometry y="330" width="140" height="30" as="geometry" />
<mxCell id="M61CuUsqLGTxYLAPg2Ho-11" value="current_action VARCHAR(16)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="350" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="M61CuUsqLGTxYLAPg2Ho-16" value="currently_viewing VARCHAR(64) --link to &quot;room_id&quot;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="7XvpWkOTBmjUhqAazzN1-1">
<mxGeometry y="360" width="140" height="50" as="geometry" />
<mxCell id="M61CuUsqLGTxYLAPg2Ho-16" value="currently_viewing VARCHAR(64) --link to &quot;room_id&quot;" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="7XvpWkOTBmjUhqAazzN1-1" vertex="1">
<mxGeometry y="380" width="140" height="50" as="geometry" />
</mxCell>
</root>
</mxGraphModel>

View File

@ -10,6 +10,8 @@ services:
source: ./config.json
target: /usr/src/app/config.json
postgresql:
healthcheck:
test: ["CMD", "pg_isready", "-U", "meets"]
image: "postgres:15"
env_file: meets.env
volumes:

View File

@ -1,5 +1,12 @@
{
"welcome": "Hello! I am a bot for new meetings in Matrix!\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/daivinchik.\n\nGood luck!",
"general": {
"welcome": "Hello! I am a bot for new meetings in Matrix!\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 ❤️), dislike (👎️ or 💔) or wanna send a messsage (💌). Write '🏠️' 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",
"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: "
},
"setup": {
"country": "Write a name of country you are living in. Prefer local language. For example, 'Россия', 'United States' or 'Україна'.",
"city": "Write a name of city you are living in. Prefer local language. For example, 'Москва', 'Washington, D.C.', or 'Київ'.",
@ -15,10 +22,12 @@
},
"errors": {
"notadm": "This room is not a DM (direct or private messages). You should invite me in a DM! By the way, I support encrypted rooms! Bye and see you in DM!",
"toobig": "Sorry! The length of what you have written here is bigger than I expected! If you beleive that it is an error, please, file an issue on my git: https://git.foxarmy.org/leca/daivinchik. Otherwise, write something a little bit smaller! Thank you!",
"toobig": "Sorry! The length of what you have written here is bigger than I expected! If you beleive that it is an error, please, file an issue on my git: https://git.foxarmy.org/leca/heart2heart. Otherwise, write something a little bit smaller! Thank you!",
"tooyoung": "Sorry! You are too young for this! Please, come back when you'll turn at least 14! Thank you and good bye!",
"toomuch": "You have loaded more that 5 pictures. I'll not upload that. Going to the next step...",
"twosexes": "There are only two sexes! Please, choose your biological one.",
"didntunderstand": "I cannot understand you answer. Please, read my question again and answer!"
"didntunderstand": "I cannot understand your answer. Please, read my question again and answer!",
"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!"
}
}

View File

@ -4,11 +4,13 @@ CREATE TABLE IF NOT EXISTS users(
pictures_urls TEXT[] DEFAULT NULL,
name VARCHAR(32) DEFAULT NULL,
age SMALLINT DEFAULT NULL,
sex BOOLEAN, -- 1 means male
interest VARCHAR(6), -- male, female or both
sex CHAR, -- 'm' of 'f'
interest CHAR, -- male, female or both ('m', 'f' and 'b')
description VARCHAR(512) DEFAULT NULL,
country VARCHAR(64) DEFAULT NULL,
city VARCHAR(64) DEFAULT NULL,
current_action VARCHAR(16) DEFAULT NULL,
currently_viewing VARCHAR(64) --link to "room_id"
currently_viewing VARCHAR(64), --link to "room_id"
liked_profiles TEXT[], -- link to "room_id"
likes_from TEXT[] -- link to room_id
);

84
src/db.js Normal file
View File

@ -0,0 +1,84 @@
import pg from 'pg'
import fs from 'fs'
const { Client } = pg
const getClient = async () => {
const client = new Client({
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
host: "postgresql",
port: 5432,
database: process.env.POSTGRES_DB
});
await client.connect()
await client.query(fs.readFileSync('./scheme.psql').toString())
return client
}
export const db = await getClient()
const getAmountOfUserPictures = async (roomId) => {
return (await db.query("SELECT cardinality(pictures_urls) AS length FROM users WHERE room_id = $1", [roomId])).rows[0].length;
}
const getCurrentUserAction = async (roomId) => {
return (await db.query('SELECT current_action FROM users WHERE room_id = $1', [roomId])).rows[0].current_action;
}
const setUserState = async (roomId, state) => {
await db.query("UPDATE users SET current_action = $1 WHERE room_id = $2", [state, roomId]);
}
const appendUserPictures = async (roomId, mxc) => {
await db.query("UPDATE users SET pictures_urls = array_append(pictures_urls, $1) WHERE room_id = $2 RETURNING cardinality(pictures_urls) AS amount", [mxc, roomId]);
}
const eraseUser = async (roomId) => {
await db.query("DELETE FROM users WHERE room_id = $1", [roomId]);
}
const selectProfilesForUser = async (roomId) => {
let userAge = (await db.query("SELECT age FROM users WHERE room_id = $1", [roomId])).rows[0].age
//Selecting profiles other than user's and with difference in age +-2.
return (await db.query("SELECT room_id, name, age, sex, description, country, city, pictures_urls FROM users WHERE age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)]) AND room_id != $2 ORDER BY RANDOM() LIMIT 1", [userAge, roomId])).rows[0]
}
const setUserCurrentlyViewingProfile = async (roomId, anotherRoomId) => {
await db.query("UPDATE users SET currently_viewing = $1 WHERE room_id = $2", [anotherRoomId, roomId]);
}
const getUserCurrentlyViewingProfile = async (roomId) => {
return (await db.query("SELECT currently_viewing FROM users WHERE room_id = $1", [roomId])).rows[0].currently_viewing
}
//Newlike is a room id of a user who was liked_profiles
const appendUserLikes = async (roomId, newLike) => {
await db.query("UPDATE users SET liked_profiles = array_append(liked_profiles, $1) WHERE room_id = $2", [newLike, roomId])
await db.query("UPDATE users SET likes_from = array_append(likes_from, $1) WHERE room_id = $2", [roomId, newLike])
}
const getUserLikes = async (roomId) => {
return (await db.query("SELECT likes FROM users WHERE room_id = $1", [roomId])).rows[0].likes
}
const checkForMutualLike = async (roomId1, roomId2) => {
return (await db.query("SELECT EXISTS (SELECT 1 FROM users u1 JOIN users u2 ON u1.room_id = ANY(u2.liked_profiles) AND u2.room_id = ANY(u1.liked_profiles) WHERE u1.room_id = $1 AND u2.room_id = $2 );", [roomId1, roomId2])).rows[0].exists
}
const getProfileInfo = async (roomId) => {
return (await db.query("SELECT mx_id, room_id, name, age, sex, description, country, city, pictures_urls FROM users WHERE room_id = $1", [roomId])).rows[0];
}
const getAllLikesForUser = async (roomId) => {
return (await db.query("SELECT room_id FROM users WHERE $1 = ANY(liked_profiles)", [roomId])).rows
}
const removeLikeFromUser = async (roomId, liked) => {
await db.query("UPDATE users SET liked_profiles = ARRAY_REMOVE(liked_profiles, $1) WHERE room_id = $2", [roomId, liked])
}
export { eraseUser, appendUserPictures, setUserState, getCurrentUserAction, getAmountOfUserPictures, selectProfilesForUser, setUserCurrentlyViewingProfile, getUserCurrentlyViewingProfile, appendUserLikes, getUserLikes, checkForMutualLike, getProfileInfo, getAllLikesForUser, removeLikeFromUser }

View File

@ -18,16 +18,27 @@ import {
} from './utils.js';
import {
getClient
} from './initDb.js'
appendUserLikes,
appendUserPictures,
checkForMutualLike,
eraseUser,
getAmountOfUserPictures,
getCurrentUserAction,
getUserCurrentlyViewingProfile,
setUserState
} from './db.js'
import { processRequest, showRandomProfileToUser, showNewLikes } from "./interactions.js";
import { db } from "./db.js"
const db = await getClient()
const config = readConfig()
const messages = readMessages()
const homeserverUrl = config.homeserverURL;
const accessToken = config.token;
const maxAmountOfPhotoesPerUser = config.maxAmountOfPhotoesPerUser
const crypto = new RustSdkCryptoStorageProvider("./bot_data/encryption_bot_sled", StoreType.Sled);
const storage = new SimpleFsStorageProvider("./bot_data/bot.json");
@ -36,115 +47,141 @@ const client = new MatrixClient(homeserverUrl, accessToken, storage, crypto);
client.on("room.message", async (roomId, event) => {
try {
if (event.content?.msgtype !== 'm.text' && event.content?.msgtype !== 'm.image') return;
if (event.sender === await client.getUserId()) return;
let current_action = (await db.query('SELECT current_action FROM users WHERE room_id = $1', [roomId])).rows[0].current_action;
let current_action = await getCurrentUserAction(roomId);
let answer = event.content.body;
switch (current_action) {
case "country":
if (answer.length > 64) {
await client.sendText(roomId, messages.errors.toobig);
return;
}
await db.query("UPDATE users SET country = $1 WHERE room_id = $2", [answer, roomId]);
await client.sendText(roomId, `Set your country setting to "${answer}".`);
await client.sendText(roomId, messages.setup.city); //next question
await db.query("UPDATE users SET current_action = 'city' WHERE room_id = $1", [roomId]);
await processRequest(client, roomId, current_action, answer, 'city');
break;
case "city":
if (answer.length > 64) {
await client.sendText(roomId, messages.errors.toobig);
return;
}
await db.query("UPDATE users SET city = $1 WHERE room_id = $2", [answer, roomId]);
await client.sendText(roomId, `Set your city setting to "${answer}".`);
await client.sendText(roomId, messages.setup.name); //next question
await db.query("UPDATE users SET current_action = 'name' WHERE room_id = $1", [roomId]);
await processRequest(client, roomId, current_action, answer, 'name');
break;
case "name":
if (answer.length > 32) {
await client.sendText(roomId, messages.errors.toobig);
return;
}
await db.query("UPDATE users SET name = $1 WHERE room_id = $2", [answer, roomId]);
await client.sendText(roomId, `Set your name setting to "${answer}".`);
await client.sendText(roomId, messages.setup.age); //next question
await db.query("UPDATE users SET current_action = 'age' WHERE room_id = $1", [roomId]);
await processRequest(client, roomId, current_action, answer, 'age');
break;
case "age":
if (answer < 14) {
await client.sendText(roomId, messages.errors.tooyoung);
await client.leaveRoom(roomId)
answer = parseInt(answer)
if (!answer) {
await client.sendText(roomId, messages.errors.didntunderstand);
return;
}
await db.query("UPDATE users SET age = $1 WHERE room_id = $2", [answer, roomId]);
await client.sendText(roomId, `Set your age setting to "${answer}".`);
await client.sendText(roomId, messages.setup.sex); //next question
await db.query("UPDATE users SET current_action = 'sex' WHERE room_id = $1", [roomId]);
if (answer < 14) {
await client.sendText(roomId, messages.errors.tooyoung);
await client.leaveRoom(roomId);
await eraseUser(roomId);
return;
}
await processRequest(client, roomId, current_action, answer, 'sex');
break;
case "sex":
answer = answer.toLowerCase().trim()
if (answer.toLowerCase() != "male" && answer.toLowerCase() != "female") {
await client.sendText(roomId, messages.errors.twosexes);
return;
}
await db.query("UPDATE users SET sex = $1 WHERE room_id = $2", [answer.toLowerCase() == "male" ? true : false, roomId]);
await client.sendText(roomId, `Set your sex setting to "${answer}".`);
await client.sendText(roomId, messages.setup.interest); //next question
await db.query("UPDATE users SET current_action = 'interest' WHERE room_id = $1", [roomId]);
await processRequest(client, roomId, current_action, answer[0], 'interest');
break;
case "interest":
if (answer.toLowerCase() != "male" && answer.toLowerCase() != "female" && answer.toLowerCase() != "both") {
answer = answer.toLowerCase().trim()
if (answer != "male" && answer != "female" && answer != "both") {
await client.sendText(roomId, messages.errors.didntunderstand);
return;
}
await db.query("UPDATE users SET interest = $1 WHERE room_id = $2", [answer.toLowerCase(), roomId]);
await client.sendText(roomId, `Set your interest setting to "${answer}".`);
await client.sendText(roomId, messages.setup.description); //next question
await db.query("UPDATE users SET current_action = 'description' WHERE room_id = $1", [roomId]);
await processRequest(client, roomId, current_action, answer[0], 'description');
break;
case "description":
if (answer.length > 512) {
await client.sendText(roomId, messages.errors.toobig);
return;
}
await db.query("UPDATE users SET description = $1 WHERE room_id = $2", [answer, roomId]);
await client.sendText(roomId, `Set your description setting to "${answer}".`);
await client.sendText(roomId, messages.setup.pictures); //next question
await db.query("UPDATE users SET current_action = 'pictures' WHERE room_id = $1", [roomId]);
await processRequest(client, roomId, current_action, answer, 'pictures');
break;
case "pictures":
if (event.content?.msgtype !== 'm.image') {
await client.sendText(roomId, messages.setup.done);
await db.query("UPDATE users SET current_action = 'view_profiles' WHERE room_id = $1", [roomId]);
await setUserState(roomId, 'view_profiles')
await showRandomProfileToUser(client, roomId);
} else {
let pictures_count = (await db.query("SELECT cardinality(pictures_urls) AS length FROM users WHERE room_id = $1", [roomId])).rows[0].length;
if (pictures_count == 5) {
let pictures_count = parseInt(await getAmountOfUserPictures(roomId));
if (pictures_count > maxAmountOfPhotoesPerUser) {
await client.sendText(roomId, messages.errors.toomuch);
await db.query("UPDATE users SET current_action = 'view_profiles' WHERE room_id = $1", [roomId]);
await setUserState(roomId, 'view_profiles')
await showRandomProfileToUser(client, roomId);
} else {
const message = new MessageEvent(event);
const fileEvent = new MessageEvent(message.raw);
const decrypted = await client.crypto.decryptMedia(fileEvent.content.file);
// const encrypted = await client.crypto.encryptMedia(Buffer.from(decrypted));
const mxc = await client.uploadContent(decrypted)
let pictures_count = (await db.query("UPDATE users SET pictures_urls = array_append(pictures_urls, $1) WHERE room_id = $2 RETURNING cardinality(pictures_urls) AS amount", [mxc, roomId])).rows[0].amount;
if (pictures_count < 5) {
await client.sendText(roomId, messages.setup.more + String(5 - pictures_count));
const mxc = await client.uploadContent(decrypted);
await appendUserPictures(roomId, mxc);
let pictures_count = await getAmountOfUserPictures(roomId);
if (pictures_count < maxAmountOfPhotoesPerUser) {
await client.sendText(roomId, messages.setup.more + String(maxAmountOfPhotoesPerUser - pictures_count));
} else {
await client.sendText(roomId, messages.setup.enough);
await db.query("UPDATE users SET current_action = 'view_profiles' WHERE room_id = $1", [roomId]);
await client.sendText(roomId, messages.setup.done);
await setUserState(roomId, 'view_profiles');
await showRandomProfileToUser(client, roomId);
}
}
}
break;
break;
case "view_profiles":
if (answer == '👍️' || answer == '❤️') {
let currently_viewing = await getUserCurrentlyViewingProfile(roomId)
await appendUserLikes(roomId, currently_viewing);
let value = await checkForMutualLike(roomId, currently_viewing);
console.log(value)
if (value) {
await client.sendText(roomId, messages.general.newlikes);
await client.sendText(currently_viewing, messages.general.newlikes);
}
} else if (answer == '💌') {
await client.sendText(roomId, messages.errors.notimplemented);
} else if (answer == '🏠️') {
await client.sendText(roomId, messages.general.menu);
await setUserState(roomId, 'menu')
return
}
await showRandomProfileToUser(client, roomId);
break;
case 'menu':
switch (answer) {
case '1':
await setUserState(roomId, 'view_profiles')
await showRandomProfileToUser(client, roomId);
break;
case '2':
// await setUserState(roomId, 'view_mut_likes');
await showNewLikes(client, roomId);
await client.sendText(roomId, messages.general.menu);
break;
case '3':
await client.sendText(roomId, messages.errors.notimplemented);
break;
default:
await client.sendText(roomId, messages.errors.didntunderstand);
break;
}
break;
// case 'view_mutual_likes':
// // await showNewLikes(client, roomId)
// break;
default:
await client.sendText(roomId, messages.errors.didntunderstand);
return;
}
} catch (e) {
logError(e)
logError(e);
}
});
client.on("room.invite", async (roomId) => {
client.on("room.leave", async (roomId, event) => {
console.log(roomId, event);
})
client.on("room.invite", async (roomId, event) => {
try {
await client.joinRoom(roomId);
@ -152,25 +189,24 @@ client.on("room.invite", async (roomId) => {
let isDM = members.length == 2 ? true : false;
if (!isDM) {
client.sendText(roomId, messages.errors.notadm);
client.leaveRoom(roomId)
await client.sendText(roomId, messages.errors.notadm);
await client.leaveRoom(roomId)
}
logInfo(`Bot has joined a room with ID ${roomId}`)
let mx_id = members[0].event.sender
let mx_id = event.sender
let is_profile_exists = (await db.query("SELECT * FROM users WHERE room_id = $1", [roomId])).rowCount > 0;
if (!is_profile_exists){
if (!is_profile_exists) {
await db.query("INSERT INTO users(mx_id, room_id, current_action) VALUES ($1, $2, $3)", [mx_id, roomId, "country"])
await client.sendText(roomId, messages.welcome);
await client.sendText(roomId, messages.general.welcome);
await client.sendText(roomId, messages.setup.country);
} else {
await db.query("UPDATE users SET current_action = 'view_profiles' WHERE room_id = $1", [roomId]);
setUserState(roomId, 'view_profiles')
}
} catch (e) {
logError(e)
}
@ -178,3 +214,27 @@ client.on("room.invite", async (roomId) => {
});
client.start().then(() => logInfo("Bot started!"));
/*
{
"body":"test",
"file": {
"url":"mxc://foxarmy.org/GSzzyXNgrSaOQjiHlnoKCQGd"
}
}
*/
/*
{
"body":"test",
"info":{
"w":256,
"h":256,
"mimetype":"image/png"
},
"file": {
"url":"mxc://foxarmy.org/GSzzyXNgrSaOQjiHlnoKCQGd"
}
}
*/

View File

@ -1,20 +0,0 @@
import pg from 'pg'
import fs from 'fs'
const { Client } = pg
export const getClient = async () => {
const client = new Client({
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
host: "postgresql",
port: 5432,
database: process.env.POSTGRES_DB
});
await client.connect()
await client.query(fs.readFileSync('./scheme.psql').toString())
return client
}

96
src/interactions.js Normal file
View File

@ -0,0 +1,96 @@
import {
logError,
logInfo,
readConfig,
readMessages
} from './utils.js';
import { db, setUserState, selectProfilesForUser, setUserCurrentlyViewingProfile, getProfileInfo, getAllLikesForUser, removeLikeFromUser, checkForMutualLike } from "./db.js"
const messages = readMessages()
const requiresLengths = {
"country": 64,
"city": 64,
"description": 512,
"name": 32,
}
const processRequest = async (client, roomId, question, answer, nextQuestion) => {
if (answer.length > requiresLengths[question]) {
await client.sendText(roomId, messages.errors.toobig);
return;
}
await db.query(`UPDATE users SET ${question} = $1 WHERE room_id = $2`, [answer, roomId]);
await client.sendText(roomId, `Set your ${question} setting to "${answer}".`);
await client.sendText(roomId, messages.setup[nextQuestion]); //next question
setUserState(roomId, nextQuestion);
}
const showRandomProfileToUser = async (client, roomId) => {
let chosenProfile = await selectProfilesForUser(roomId);
if (!chosenProfile) {
await client.sendText(roomId, messages.errors.noprofiles);
return;
}
let message =
`${chosenProfile.country}, ${chosenProfile.city}.
${chosenProfile.name}, ${chosenProfile.sex == 'm' ? 'male' : 'female'}, ${chosenProfile.age}.
${chosenProfile.description}`
await setUserCurrentlyViewingProfile(roomId, chosenProfile.room_id)
await client.sendText(roomId, message);
if (chosenProfile.pictures_urls) {
for (let picture of chosenProfile.pictures_urls) {
console.log(picture)
await client.sendMessage(roomId, {
msgtype: "m.image",
body: "image.png",
url: picture
});
}
}
await client.sendText(roomId, messages.general.rate)
}
const showProfileToUser = async (client, roomId, profileId) => {
let profileInfo = await getProfileInfo(profileId);
console.log(profileInfo)
let message =
`${profileInfo.country}, ${profileInfo.city}.
${profileInfo.name}, ${profileInfo.sex == 'm' ? 'male' : 'female'}, ${profileInfo.age}.
${profileInfo.description}`
await client.sendText(roomId, messages.general.showalike);
await client.sendText(roomId, message);
if (profileInfo.pictures_urls) {
for (let picture of profileInfo.pictures_urls) {
console.log(picture)
await client.sendMessage(roomId, {
msgtype: "m.image",
body: "image.png",
url: picture
});
}
}
await client.sendText(roomId, messages.general.mxid + profileInfo.mx_id)
}
const showNewLikes = async (client, roomId) => {
let likes = (await getAllLikesForUser(roomId));
console.log(`likes`);
console.log(likes)
for (let liked of likes) {
// if (await checkForMutualLike(liked.roomId, roomId)) {
await showProfileToUser(client, roomId, liked.room_id);
await removeLikeFromUser(roomId, liked.room_id);
// }
}
}
export { processRequest, showRandomProfileToUser, showNewLikes, showProfileToUser };

View File

@ -3,17 +3,17 @@ import fs from 'fs'
const configPath = './config.json'
const messagesPath = './messages.json'
export const logError = (message) => {
const logError = (message) => {
let time = new Date
console.error(`[${time.toLocaleString()}] [LOG] [E] ${message}`)
}
export const logInfo = (message) => {
const logInfo = (message) => {
let time = new Date
console.log(`[${time.toLocaleString()}] [LOG] [I] ${message}`)
}
export const readConfig = () => {
const readConfig = () => {
if (!fs.existsSync(configPath)) {
@ -33,10 +33,12 @@ export const readConfig = () => {
)
}
export const readMessages = () => {
const readMessages = () => {
if (!fs.existsSync(messagesPath)) {
logError("No 'messages.json' file found. Please, ensure that you are up to date by syncing using 'git pull' command.")
process.exit(-1)
}
return JSON.parse(fs.readFileSync(messagesPath))
}
}
export {readMessages, readConfig, logError, logInfo};