{ "version": 3, "sources": ["../../../server/friends.ts"], "sourcesContent": ["/**\r\n * Friends chat-plugin database handler.\r\n * @author mia-pi-git\r\n */\r\n// @ts-ignore in case it isn't installed\r\nimport type * as Database from 'better-sqlite3';\r\nimport {Utils, FS, ProcessManager, Repl} from '../lib';\r\nimport {Config} from './config-loader';\r\nimport * as path from 'path';\r\n\r\n/** Max friends per user */\r\nexport const MAX_FRIENDS = 100;\r\n/** Max friend requests. */\r\nexport const MAX_REQUESTS = 6;\r\nexport const DEFAULT_FILE = FS('databases/friends.db').path;\r\nconst REQUEST_EXPIRY_TIME = 30 * 24 * 60 * 60 * 1000;\r\nconst PM_TIMEOUT = 30 * 60 * 1000;\r\n\r\nexport interface DatabaseRequest {\r\n\tstatement: string;\r\n\ttype: 'all' | 'get' | 'run' | 'transaction';\r\n\tdata: AnyObject | any[];\r\n}\r\n\r\nexport interface DatabaseResult {\r\n\t/** Specify this to return an error message to the user */\r\n\terror?: string;\r\n\tresult?: any;\r\n}\r\n\r\nexport interface Friend {\r\n\t/** Always the same as Friend#friend. Use whichever you want. */\r\n\tuserid: ID;\r\n\t/** Always the same as Friend#userid. Use whichever you want. */\r\n\tfriend: ID;\r\n\tsend_login_data: number;\r\n\tlast_login: number;\r\n\tpublic_list: number;\r\n\tallowing_login: number;\r\n}\r\n\r\n/** Like Chat.ErrorMessage, but made for the subprocess so we can throw errors to the user not using errorMessage\r\n * because errorMessage crashes when imported (plus we have to spawn dex, etc, all unnecessary - this is easier)\r\n */\r\nexport class FailureMessage extends Error {\r\n\tconstructor(message: string) {\r\n\t\tsuper(message);\r\n\t\tthis.name = 'FailureMessage';\r\n\t\tError.captureStackTrace(this, FailureMessage);\r\n\t}\r\n}\r\n\r\nexport function sendPM(message: string, to: string, from = '&') {\r\n\tconst senderID = toID(to);\r\n\tconst receiverID = toID(from);\r\n\tconst sendingUser = Users.get(senderID);\r\n\tconst receivingUser = Users.get(receiverID);\r\n\tconst fromIdentity = sendingUser ? sendingUser.getIdentity() : ` ${senderID}`;\r\n\tconst toIdentity = receivingUser ? receivingUser.getIdentity() : ` ${receiverID}`;\r\n\r\n\tif (from === '&') {\r\n\t\treturn sendingUser?.send(`|pm|&|${toIdentity}|${message}`);\r\n\t}\r\n\tif (sendingUser) {\r\n\t\tsendingUser.send(`|pm|${fromIdentity}|${toIdentity}|${message}`);\r\n\t}\r\n\tif (receivingUser) {\r\n\t\treceivingUser.send(`|pm|${fromIdentity}|${toIdentity}|${message}`);\r\n\t}\r\n}\r\n\r\nfunction canPM(sender: User, receiver: User | null) {\r\n\tif (!receiver || !receiver.settings.blockPMs) return true;\r\n\tif (receiver.settings.blockPMs === true) return sender.can('lock');\r\n\tif (receiver.settings.blockPMs === 'friends') return false;\r\n\treturn Users.globalAuth.atLeast(sender, receiver.settings.blockPMs);\r\n}\r\n\r\nexport class FriendsDatabase {\r\n\tfile: string;\r\n\tconstructor(file: string = DEFAULT_FILE) {\r\n\t\tthis.file = file === ':memory:' ? file : path.resolve(file);\r\n\t}\r\n\tasync updateUserCache(user: User) {\r\n\t\tuser.friends = new Set(); // we clear to account for users who may have been deleted\r\n\t\tconst friends = await this.getFriends(user.id);\r\n\t\tfor (const friend of friends) {\r\n\t\t\tuser.friends.add(friend.userid);\r\n\t\t}\r\n\t\treturn user.friends;\r\n\t}\r\n\tstatic setupDatabase(fileName?: string) {\r\n\t\tconst file = fileName || process.env.filename || DEFAULT_FILE;\r\n\t\tconst exists = FS(file).existsSync() || file === ':memory:';\r\n\t\tconst database: Database.Database = new (require('better-sqlite3'))(file);\r\n\t\tif (!exists) {\r\n\t\t\tdatabase.exec(FS('databases/schemas/friends.sql').readSync());\r\n\t\t} else {\r\n\t\t\tlet val;\r\n\t\t\ttry {\r\n\t\t\t\tval = database.prepare(`SELECT val FROM database_settings WHERE name = 'version'`).get().val;\r\n\t\t\t} catch {}\r\n\t\t\tconst actualVersion = FS(`databases/migrations/friends`).readdirIfExistsSync().length;\r\n\t\t\tif (val === undefined) {\r\n\t\t\t\t// hasn't been set up before, write new version.\r\n\t\t\t\tdatabase.exec(FS('databases/schemas/friends.sql').readSync());\r\n\t\t\t}\r\n\t\t\tif (typeof val === 'number' && val !== actualVersion) {\r\n\t\t\t\tthrow new Error(`Friends DB is out of date, please migrate to latest version.`);\r\n\t\t\t}\r\n\t\t}\r\n\t\tdatabase.exec(FS(`databases/schemas/friends-startup.sql`).readSync());\r\n\r\n\t\tfor (const k in FUNCTIONS) {\r\n\t\t\tdatabase.function(k, FUNCTIONS[k]);\r\n\t\t}\r\n\r\n\t\tfor (const k in ACTIONS) {\r\n\t\t\ttry {\r\n\t\t\t\tstatements[k] = database.prepare(ACTIONS[k as keyof typeof ACTIONS]);\r\n\t\t\t} catch (e: any) {\r\n\t\t\t\tthrow new Error(`Friends DB statement crashed: ${ACTIONS[k as keyof typeof ACTIONS]} (${e.message})`);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (const k in TRANSACTIONS) {\r\n\t\t\ttransactions[k] = database.transaction(TRANSACTIONS[k]);\r\n\t\t}\r\n\r\n\t\tstatements.expire.run();\r\n\t\treturn database;\r\n\t}\r\n\tasync getFriends(userid: ID): Promise {\r\n\t\treturn (await this.all('get', [userid, MAX_FRIENDS])) || [];\r\n\t}\r\n\tasync getRequests(user: User) {\r\n\t\tconst sent: Set = new Set();\r\n\t\tconst received: Set = new Set();\r\n\t\tif (user.settings.blockFriendRequests) {\r\n\t\t\t// delete any pending requests that may have been sent to them while offline\r\n\t\t\t// we used to return but we will not since you can send requests while blocking\r\n\t\t\tawait this.run('deleteReceivedRequests', [user.id]);\r\n\t\t}\r\n\t\tconst sentResults = await this.all('getSent', [user.id]);\r\n\t\tif (sentResults === null) return {sent, received};\r\n\t\tfor (const request of sentResults) {\r\n\t\t\tsent.add(request.receiver);\r\n\t\t}\r\n\t\tconst receivedResults = await this.all('getReceived', [user.id]) || [];\r\n\t\tif (!Array.isArray(receivedResults)) {\r\n\t\t\tMonitor.crashlog(new Error(\"Malformed results received\"), 'A friends process', {\r\n\t\t\t\tuser: user.id,\r\n\t\t\t\tresult: JSON.stringify(receivedResults),\r\n\t\t\t});\r\n\t\t\treturn {received, sent};\r\n\t\t}\r\n\t\tfor (const request of receivedResults) {\r\n\t\t\treceived.add(request.sender);\r\n\t\t}\r\n\t\treturn {sent, received};\r\n\t}\r\n\tall(statement: string, data: any[] | AnyObject) {\r\n\t\treturn this.query({type: 'all', data, statement});\r\n\t}\r\n\ttransaction(statement: string, data: any[] | AnyObject) {\r\n\t\treturn this.query({data, statement, type: 'transaction'});\r\n\t}\r\n\trun(statement: string, data: any[] | AnyObject) {\r\n\t\treturn this.query({statement, data, type: 'run'});\r\n\t}\r\n\tget(statement: string, data: any[] | AnyObject) {\r\n\t\treturn this.query({statement, data, type: 'get'});\r\n\t}\r\n\tprivate async query(input: DatabaseRequest) {\r\n\t\tconst process = PM.acquire();\r\n\t\tif (!process || !Config.usesqlite) {\r\n\t\t\treturn {result: null};\r\n\t\t}\r\n\t\tconst result = await process.query(input);\r\n\t\tif (result.error) {\r\n\t\t\tthrow new Chat.ErrorMessage(result.error);\r\n\t\t}\r\n\t\treturn result.result;\r\n\t}\r\n\tasync request(user: User, receiverID: ID) {\r\n\t\tconst receiver = Users.getExact(receiverID);\r\n\t\tif (receiverID === user.id || receiver?.previousIDs.includes(user.id)) {\r\n\t\t\tthrow new Chat.ErrorMessage(`You can't friend yourself.`);\r\n\t\t}\r\n\t\tif (receiver?.settings.blockFriendRequests) {\r\n\t\t\tthrow new Chat.ErrorMessage(`${receiver.name} is blocking friend requests.`);\r\n\t\t}\r\n\t\tlet buf = Utils.html`/uhtml sent-${user.id}, | `;\r\n\t\tbuf += Utils.html`
`;\r\n\t\tbuf += `(You can also stop this user from sending you friend requests with /ignore)`;\r\n\t\tconst disclaimer = (\r\n\t\t\t`/raw Note: If this request is accepted, your friend will be notified when you come online, ` +\r\n\t\t\t`and you will be notified when they do, unless you opt out of receiving them.`\r\n\t\t);\r\n\t\tif (receiver?.settings.blockFriendRequests) {\r\n\t\t\tthrow new Chat.ErrorMessage(`This user is blocking friend requests.`);\r\n\t\t}\r\n\t\tif (!canPM(user, receiver)) {\r\n\t\t\tthrow new Chat.ErrorMessage(`This user is blocking PMs, and cannot be friended right now.`);\r\n\t\t}\r\n\r\n\t\tconst result = await this.transaction('send', [user.id, receiverID]);\r\n\t\tif (receiver) {\r\n\t\t\tsendPM(`/raw ${user.name} sent you a friend request!`, receiver.id);\r\n\t\t\tsendPM(buf, receiver.id);\r\n\t\t\tsendPM(disclaimer, receiver.id);\r\n\t\t}\r\n\t\tsendPM(\r\n\t\t\t`/nonotify You sent a friend request to ${receiver?.connected ? receiver.name : receiverID}!`,\r\n\t\t\tuser.name\r\n\t\t);\r\n\t\tsendPM(\r\n\t\t\t`/uhtml undo-${receiverID},`, user.name\r\n\t\t);\r\n\t\tsendPM(disclaimer, user.id);\r\n\t\treturn result;\r\n\t}\r\n\tasync removeRequest(receiverID: ID, senderID: ID) {\r\n\t\tif (!senderID) throw new Chat.ErrorMessage(`Invalid sender username.`);\r\n\t\tif (!receiverID) throw new Chat.ErrorMessage(`Invalid receiver username.`);\r\n\r\n\t\treturn this.run('deleteRequest', [senderID, receiverID]);\r\n\t}\r\n\tasync approveRequest(receiverID: ID, senderID: ID) {\r\n\t\treturn this.transaction('accept', [senderID, receiverID]);\r\n\t}\r\n\tasync removeFriend(userid: ID, friendID: ID) {\r\n\t\tif (!friendID || !userid) throw new Chat.ErrorMessage(`Invalid usernames supplied.`);\r\n\r\n\t\tconst result = await this.run('delete', {user1: userid, user2: friendID});\r\n\t\tif (result.changes < 1) {\r\n\t\t\tthrow new Chat.ErrorMessage(`You do not have ${friendID} friended.`);\r\n\t\t}\r\n\t}\r\n\twriteLogin(user: ID) {\r\n\t\treturn this.run('login', [user, Date.now(), Date.now()]);\r\n\t}\r\n\thideLoginData(id: ID) {\r\n\t\treturn this.run('hideLogin', [id, Date.now()]);\r\n\t}\r\n\tallowLoginData(id: ID) {\r\n\t\treturn this.run('showLogin', [id]);\r\n\t}\r\n\tasync getLastLogin(userid: ID) {\r\n\t\tconst result = await this.get('checkLastLogin', [userid]);\r\n\t\treturn parseInt(result?.['last_login']) || null;\r\n\t}\r\n\tasync getSettings(userid: ID) {\r\n\t\treturn (await this.get('getSettings', [userid])) || {};\r\n\t}\r\n\tsetHideList(userid: ID, setting: boolean) {\r\n\t\tconst num = setting ? 1 : 0;\r\n\t\t// name, send_login_data, last_login, public_list\r\n\t\treturn this.run('toggleList', [userid, num, num]);\r\n\t}\r\n}\r\n\r\nconst statements: {[k: string]: Database.Statement} = {};\r\nconst transactions: {[k: string]: Database.Transaction} = {};\r\n\r\nconst ACTIONS = {\r\n\tadd: (\r\n\t\t`REPLACE INTO friends (user1, user2) VALUES ($user1, $user2) ON CONFLICT (user1, user2) ` +\r\n\t\t`DO UPDATE SET user1 = $user1, user2 = $user2`\r\n\t),\r\n\tget: (\r\n\t\t`SELECT * FROM friends_simplified f LEFT JOIN friend_settings fs ON f.friend = fs.userid WHERE f.userid = ? LIMIT ?`\r\n\t),\r\n\tdelete: `DELETE FROM friends WHERE (user1 = $user1 AND user2 = $user2) OR (user1 = $user2 AND user2 = $user1)`,\r\n\tgetSent: `SELECT receiver, sender FROM friend_requests WHERE sender = ?`,\r\n\tgetReceived: `SELECT receiver, sender FROM friend_requests WHERE receiver = ?`,\r\n\tinsertRequest: `INSERT INTO friend_requests(sender, receiver, sent_at) VALUES (?, ?, ?)`,\r\n\tdeleteRequest: `DELETE FROM friend_requests WHERE sender = ? AND receiver = ?`,\r\n\tdeleteReceivedRequests: `DELETE FROM friend_requests WHERE receiver = ?`,\r\n\tfindFriendship: `SELECT * FROM friends WHERE (user1 = $user1 AND user2 = $user2) OR (user2 = $user1 AND user1 = $user2)`,\r\n\tfindRequest: (\r\n\t\t`SELECT count(*) as num FROM friend_requests WHERE ` +\r\n\t\t`(sender = $user1 AND receiver = $user2) OR (sender = $user2 AND receiver = $user1)`\r\n\t),\r\n\tcountRequests: `SELECT count(*) as num FROM friend_requests WHERE (sender = ? OR receiver = ?)`,\r\n\tlogin: (\r\n\t\t`INSERT INTO friend_settings (userid, send_login_data, last_login, public_list) VALUES (?, 0, ?, 0) ` +\r\n\t\t`ON CONFLICT (userid) DO UPDATE SET last_login = ?`\r\n\t),\r\n\tcheckLastLogin: `SELECT last_login FROM friend_settings WHERE userid = ?`,\r\n\tdeleteLogin: `UPDATE friend_settings SET last_login = 0 WHERE userid = ?`,\r\n\texpire: (\r\n\t\t`DELETE FROM friend_requests WHERE EXISTS` +\r\n\t\t`(SELECT sent_at FROM friend_requests WHERE should_expire(sent_at) = 1)`\r\n\t),\r\n\thideLogin: ( // this works since if the insert works, they have no data, which means no public_list\r\n\t\t`INSERT INTO friend_settings (userid, send_login_data, last_login, public_list) VALUES (?, 1, ?, 0) ` +\r\n\t\t`ON CONFLICT (userid) DO UPDATE SET send_login_data = 1`\r\n\t),\r\n\tshowLogin: `DELETE FROM friend_settings WHERE userid = ? AND send_login_data = 1`,\r\n\tcountFriends: `SELECT count(*) as num FROM friends WHERE (user1 = ? OR user2 = ?)`,\r\n\tgetSettings: `SELECT * FROM friend_settings WHERE userid = ?`,\r\n\ttoggleList: (\r\n\t\t`INSERT INTO friend_settings (userid, send_login_data, last_login, public_list) VALUES (?, 0, 0, ?) ` +\r\n\t\t`ON CONFLICT (userid) DO UPDATE SET public_list = ?`\r\n\t),\r\n};\r\n\r\nconst FUNCTIONS: {[k: string]: (...input: any[]) => any} = {\r\n\t'should_expire': (sentTime: number) => {\r\n\t\tif (Date.now() - sentTime > REQUEST_EXPIRY_TIME) return 1;\r\n\t\treturn 0;\r\n\t},\r\n};\r\n\r\nconst TRANSACTIONS: {[k: string]: (input: any[]) => DatabaseResult} = {\r\n\tsend: requests => {\r\n\t\tfor (const request of requests) {\r\n\t\t\tconst [senderID, receiverID] = request;\r\n\t\t\tconst hasSentRequest = statements.findRequest.get({user1: senderID, user2: receiverID})['num'];\r\n\t\t\tconst friends = statements.countFriends.get(senderID, senderID)['num'];\r\n\t\t\tconst totalRequests = statements.countRequests.get(senderID, senderID)['num'];\r\n\t\t\tif (friends >= MAX_FRIENDS) {\r\n\t\t\t\tthrow new FailureMessage(`You are at the maximum number of friends.`);\r\n\t\t\t}\r\n\t\t\tconst existingFriendship = statements.findFriendship.all({user1: senderID, user2: receiverID});\r\n\t\t\tif (existingFriendship.length) {\r\n\t\t\t\tthrow new FailureMessage(`You are already friends with '${receiverID}'.`);\r\n\t\t\t}\r\n\t\t\tif (hasSentRequest) {\r\n\t\t\t\tthrow new FailureMessage(`You have already sent a friend request to '${receiverID}'.`);\r\n\t\t\t}\r\n\t\t\tif (totalRequests >= MAX_REQUESTS) {\r\n\t\t\t\tthrow new FailureMessage(\r\n\t\t\t\t\t`You already have ${MAX_REQUESTS} pending friend requests. Use \"/friends view sent\" to see your outgoing requests and \"/friends view receive\" to see your incoming requests.`\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\tstatements.insertRequest.run(senderID, receiverID, Date.now());\r\n\t\t}\r\n\t\treturn {result: []};\r\n\t},\r\n\tadd: requests => {\r\n\t\tfor (const request of requests) {\r\n\t\t\tconst [senderID, receiverID] = request;\r\n\t\t\tstatements.add.run({user1: senderID, user2: receiverID});\r\n\t\t}\r\n\t\treturn {result: []};\r\n\t},\r\n\taccept: requests => {\r\n\t\tfor (const request of requests) {\r\n\t\t\tconst [senderID, receiverID] = request;\r\n\t\t\tconst friends = statements.get.all(receiverID, 101);\r\n\t\t\tif (friends?.length >= MAX_FRIENDS) {\r\n\t\t\t\tthrow new FailureMessage(`You are at the maximum number of friends.`);\r\n\t\t\t}\r\n\t\t\tconst {result} = TRANSACTIONS.removeRequest([request]);\r\n\t\t\tif (!result.length) throw new FailureMessage(`You have no request pending from ${senderID}.`);\r\n\t\t\tTRANSACTIONS.add([request]);\r\n\t\t}\r\n\t\treturn {result: []};\r\n\t},\r\n\tremoveRequest: requests => {\r\n\t\tconst result = [];\r\n\t\tfor (const request of requests) {\r\n\t\t\tconst [to, from] = request;\r\n\t\t\tconst {changes} = statements.deleteRequest.run(to, from);\r\n\t\t\tif (changes) result.push(changes);\r\n\t\t}\r\n\t\treturn {result};\r\n\t},\r\n};\r\n\r\nexport const PM = new ProcessManager.QueryProcessManager(module, query => {\r\n\tconst {type, statement, data} = query;\r\n\tconst start = Date.now();\r\n\tconst result: DatabaseResult = {};\r\n\ttry {\r\n\t\tswitch (type) {\r\n\t\tcase 'run':\r\n\t\t\tresult.result = statements[statement].run(data);\r\n\t\t\tbreak;\r\n\t\tcase 'get':\r\n\t\t\tresult.result = statements[statement].get(data);\r\n\t\t\tbreak;\r\n\t\tcase 'transaction':\r\n\t\t\tresult.result = transactions[statement]([data]);\r\n\t\t\tbreak;\r\n\t\tcase 'all':\r\n\t\t\tresult.result = statements[statement].all(data);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t} catch (e: any) {\r\n\t\tif (!e.name.endsWith('FailureMessage')) {\r\n\t\t\tresult.error = \"Sorry! The database process crashed. We've been notified and will fix this.\";\r\n\t\t\tMonitor.crashlog(e, \"A friends database process\", query);\r\n\t\t} else {\r\n\t\t\tresult.error = e.message;\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\tconst delta = Date.now() - start;\r\n\tif (delta > 1000) {\r\n\t\tMonitor.slow(`[Slow friends list query] ${JSON.stringify(query)}`);\r\n\t}\r\n\treturn result;\r\n}, PM_TIMEOUT, message => {\r\n\tif (message.startsWith('SLOW\\n')) {\r\n\t\tMonitor.slow(message.slice(5));\r\n\t}\r\n});\r\n\r\nif (require.main === module) {\r\n\tglobal.Config = (require as any)('./config-loader').Config;\r\n\tif (Config.usesqlite) {\r\n\t\tFriendsDatabase.setupDatabase();\r\n\t}\r\n\tglobal.Monitor = {\r\n\t\tcrashlog(error: Error, source = 'A friends database process', details: AnyObject | null = null) {\r\n\t\t\tconst repr = JSON.stringify([error.name, error.message, source, details]);\r\n\t\t\tprocess.send!(`THROW\\n@!!@${repr}\\n${error.stack}`);\r\n\t\t},\r\n\t\tslow(message: string) {\r\n\t\t\tprocess.send!(`CALLBACK\\nSLOW\\n${message}`);\r\n\t\t},\r\n\t};\r\n\tprocess.on('uncaughtException', err => {\r\n\t\tif (Config.crashguard) {\r\n\t\t\tMonitor.crashlog(err, 'A friends child process');\r\n\t\t}\r\n\t});\r\n\t// eslint-disable-next-line no-eval\r\n\tRepl.start(`friends-${process.pid}`, cmd => eval(cmd));\r\n} else if (!process.send) {\r\n\tPM.spawn(Config.friendsprocesses || 1);\r\n}\r\n"], "mappings": "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", "names": ["process"] }