Spaces:
Running
Running
{ | |
"version": 3, | |
"sources": ["../../../server/chat-plugins/friends.ts"], | |
"sourcesContent": ["/**\n * Friends list plugin.\n * Allows for adding and removing friends, as well as seeing their activity.\n * Written by Mia.\n * @author mia-pi-git\n */\n\nimport { Utils } from '../../lib/utils';\nimport { MAX_REQUESTS, sendPM } from '../friends';\n\nconst STATUS_COLORS: { [k: string]: string } = {\n\tidle: '#ff7000',\n\tonline: '#009900',\n\tbusy: '#cc3838',\n};\n\nconst STATUS_TITLES: { [k: string]: string } = {\n\tonline: 'Online',\n\tidle: 'Idle',\n\tbusy: 'Busy',\n\toffline: 'Offline',\n};\n\n// once every 15 minutes\nconst LOGIN_NOTIFY_THROTTLE = 15 * 60 * 1000;\n\nexport const Friends = new class {\n\tasync notifyPending(user: User) {\n\t\tif (user.settings.blockFriendRequests) return;\n\t\tconst friendRequests = await Chat.Friends.getRequests(user);\n\t\tconst pendingCount = friendRequests.received.size;\n\t\tif (pendingCount < 1) return;\n\t\tif (pendingCount === 1) {\n\t\t\tconst sender = [...friendRequests.received][0];\n\t\t\tconst senderName = Users.getExact(sender)?.name || sender;\n\t\t\tlet buf = Utils.html`/uhtml sent,<button class=\"button\" name=\"send\" value=\"/friends accept ${sender}\">Accept</button> | `;\n\t\t\tbuf += Utils.html`<button class=\"button\" name=\"send\" value=\"/friends reject ${sender}\">Deny</button><br /> `;\n\t\t\tbuf += `<small>(You can also stop this user from sending you friend requests with <code>/ignore</code>)</small>`;\n\t\t\tsendPM(Utils.html`/raw <span class=\"username\">${senderName}</span> sent you a friend request!`, user.id);\n\t\t\tsendPM(buf, user.id);\n\t\t\tsendPM(\n\t\t\t\t`/raw <small>Note: If this request is accepted, your friend will be notified when you come online, ` +\n\t\t\t\t`and you will be notified when they do, unless you opt out of receiving them.</small>`,\n\t\t\t\tuser.id\n\t\t\t);\n\t\t} else {\n\t\t\tsendPM(`/nonotify You have ${pendingCount} friend requests pending!`, user.id);\n\t\t\tsendPM(`/raw <button class=\"button\" name=\"send\" value=\"/j view-friends-received\">View</button></div>`, user.id);\n\t\t}\n\t}\n\tasync notifyConnection(user: User) {\n\t\tconst connected = await Chat.Friends.getLastLogin(user.id);\n\t\tif (connected && (Date.now() - connected) < LOGIN_NOTIFY_THROTTLE) {\n\t\t\treturn;\n\t\t}\n\t\tconst friends = await Chat.Friends.getFriends(user.id);\n\t\tconst message = `/nonotify Your friend <username class=\"username\">${Utils.escapeHTML(user.name)}</username> has just connected!`;\n\t\tfor (const f of friends) {\n\t\t\tconst curUser = Users.getExact(f.friend);\n\t\t\tif (curUser?.settings.allowFriendNotifications) {\n\t\t\t\tcurUser.send(`|pm|~|${curUser.getIdentity()}|${message}`);\n\t\t\t}\n\t\t}\n\t}\n\tasync visualizeList(userid: ID) {\n\t\tconst friends = await Chat.Friends.getFriends(userid);\n\t\tconst categorized: { [k: string]: string[] } = {\n\t\t\tonline: [],\n\t\t\tidle: [],\n\t\t\tbusy: [],\n\t\t\toffline: [],\n\t\t};\n\t\tconst loginTimes: { [k: string]: number } = {};\n\t\tfor (const { friend: friendID, last_login, allowing_login: hideLogin } of [...friends].sort()) {\n\t\t\tconst friend = Users.getExact(friendID);\n\t\t\tif (friend?.connected) {\n\t\t\t\tcategorized[friend.statusType].push(friend.id);\n\t\t\t} else {\n\t\t\t\tcategorized.offline.push(friendID);\n\t\t\t\t// hidelogin - 1 to disable it being visible\n\t\t\t\tif (!hideLogin) {\n\t\t\t\t\tloginTimes[toID(friendID)] = last_login;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst sorted = Object.keys(categorized)\n\t\t\t.filter(item => categorized[item].length > 0)\n\t\t\t.map(item => `${STATUS_TITLES[item]} (${categorized[item].length})`);\n\n\t\tlet buf = `<h3>Your friends: `;\n\t\tif (sorted.length > 0) {\n\t\t\tbuf += `<small> Total (${friends.length}) | ${sorted.join(' | ')}</small></h3> `;\n\t\t} else {\n\t\t\tbuf += `</h3><em>you have no friends added on Showdown lol</em><br /><br /><br />`;\n\t\t\tbuf += `<strong>To add a friend, use </strong><code>/friend add [username]</code>.<br /><br />`;\n\t\t\treturn buf;\n\t\t}\n\n\t\tbuf += `<form data-submitsend=\"/friend add {username}\">Add friend: <input class=\"textbox\" name=\"username\" /><br />`;\n\t\tbuf += `<button class=\"button\" type=\"submit\">Add <i class=\"fa fa-paper-plane\"></i></button></form>`;\n\n\t\tfor (const key in categorized) {\n\t\t\tconst friendArray = categorized[key].sort();\n\t\t\tif (friendArray.length === 0) continue;\n\t\t\tbuf += `<h4>${STATUS_TITLES[key]} (${friendArray.length})</h4>`;\n\t\t\tfor (const friend of friendArray) {\n\t\t\t\tconst friendID = toID(friend);\n\t\t\t\tbuf += `<div class=\"pad\"><div>`;\n\t\t\t\tbuf += this.displayFriend(friendID, loginTimes[friendID]);\n\t\t\t\tbuf += `</div></div>`;\n\t\t\t}\n\t\t}\n\n\t\treturn buf;\n\t}\n\t// much more info redacted\n\tasync visualizePublicList(userid: ID) {\n\t\tconst friends: string[] = (await Chat.Friends.getFriends(userid) as any[]).map(f => f.friend);\n\t\tlet buf = `<h3>${userid}'s friends:</h3><hr />`;\n\t\tif (!friends.length) {\n\t\t\tbuf += `None.`;\n\t\t\treturn buf;\n\t\t}\n\t\tfor (const friend of friends) {\n\t\t\tbuf += `- <username>${friend}</username><br />`;\n\t\t}\n\t\treturn buf;\n\t}\n\tdisplayFriend(userid: ID, login?: number) {\n\t\tconst user = Users.getExact(userid); // we want this to be exact\n\t\tconst name = Utils.escapeHTML(user ? user.name : userid);\n\t\tconst statusType = user?.connected ?\n\t\t\t`<strong style=\"color:${STATUS_COLORS[user.statusType]}\">\\u25C9 ${STATUS_TITLES[user.statusType]}</strong>` :\n\t\t\t'\\u25CC Offline';\n\t\tlet buf = user ?\n\t\t\t`<span class=\"username\"> <username>${name}</username></span><span><small> (${statusType})</small></span>` :\n\t\t\tUtils.html`<i>${name}</i> <small>(${statusType})</small>`;\n\t\tbuf += `<br />`;\n\n\t\tconst curUser = Users.get(userid); // might be an alt\n\t\tif (user) {\n\t\t\tif (user.userMessage) buf += Utils.html`Status: <i>${user.userMessage}</i><br />`;\n\t\t} else if (curUser && curUser.id !== userid) {\n\t\t\tbuf += `<small>On an alternate account</small><br />`;\n\t\t}\n\t\tif (login && typeof login === 'number' && !user?.connected) {\n\t\t\tbuf += `Last seen: <time>${new Date(Number(login)).toISOString()}</time>`;\n\t\t\tbuf += ` (${Chat.toDurationString(Date.now() - login, { precision: 1 })} ago)`;\n\t\t} else if (typeof login === 'string') {\n\t\t\tbuf += `${login}`;\n\t\t}\n\t\tbuf = `<div class=\"infobox\">${buf}</div>`;\n\t\treturn toLink(buf);\n\t}\n\tcheckCanUse(context: Chat.CommandContext | Chat.PageContext) {\n\t\tconst user = context.user;\n\t\tif (!user.autoconfirmed) {\n\t\t\tthrow new Chat.ErrorMessage(context.tr`You must be autoconfirmed to use the friends feature.`);\n\t\t}\n\t\tif (user.locked || user.namelocked || user.semilocked || user.permalocked) {\n\t\t\tthrow new Chat.ErrorMessage(`You are locked, and so cannot use the friends feature.`);\n\t\t}\n\t\tif (!Config.usesqlitefriends || !Config.usesqlite) {\n\t\t\tthrow new Chat.ErrorMessage(`The friends list feature is currently disabled.`);\n\t\t}\n\t\tif (!Users.globalAuth.atLeast(user, Config.usesqlitefriends)) {\n\t\t\tthrow new Chat.ErrorMessage(`You are currently unable to use the friends feature.`);\n\t\t}\n\t}\n\trequest(user: User, receiver: ID) {\n\t\treturn Chat.Friends.request(user, receiver);\n\t}\n\tremoveFriend(userid: ID, friendID: ID) {\n\t\treturn Chat.Friends.removeFriend(userid, friendID);\n\t}\n\tapproveRequest(receiverID: ID, senderID: ID) {\n\t\treturn Chat.Friends.approveRequest(receiverID, senderID);\n\t}\n\tremoveRequest(receiverID: ID, senderID: ID) {\n\t\treturn Chat.Friends.removeRequest(receiverID, senderID);\n\t}\n\tupdateSpectatorLists(user: User) {\n\t\tif (!user.friends) return; // probably should never happen\n\t\tfor (const id of user.friends) {\n\t\t\t// should only work if theyre on that userid, since friends list is by userid\n\t\t\tconst curUser = Users.getExact(id);\n\t\t\tif (curUser) {\n\t\t\t\tfor (const conn of curUser.connections) {\n\t\t\t\t\tif (conn.openPages?.has('friends-spectate')) {\n\t\t\t\t\t\tvoid Chat.parse('/friends view spectate', null, curUser, conn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\n/** UI functions chiefly for the chat page. */\n\nfunction toLink(buf: string) {\n\treturn buf.replace(/<a roomid=\"/g, `<a target=\"replace\" href=\"/`);\n}\n\nfunction headerButtons(type: string, user: User) {\n\tconst buf = [];\n\tconst icons: { [k: string]: string } = {\n\t\tsent: '<i class=\"fa fa-paper-plane\"></i>',\n\t\treceived: '<i class=\"fa fa-get-pocket\"></i>',\n\t\tall: '<i class=\"fa fa-users\"></i>',\n\t\thelp: '<i class=\"fa fa-question-circle\"></i>',\n\t\tsettings: '<i class=\"fa fa-cog\"></i>',\n\t\tspectate: '<i class=\"fa fa-binoculars\"></i>',\n\t};\n\tconst titles: { [k: string]: string } = {\n\t\tall: 'All Friends',\n\t\tspectate: 'Spectate',\n\t\tsent: 'Sent',\n\t\treceived: 'Received',\n\t\thelp: 'Help',\n\t\tsettings: 'Settings',\n\t};\n\tfor (const page in titles) {\n\t\tconst title = titles[page];\n\t\tconst icon = icons[page];\n\t\tif (page === type) {\n\t\t\tbuf.push(`${icon} <strong>${user.tr(title)}</strong>`);\n\t\t} else {\n\t\t\tbuf.push(`${icon} <a roomid=\"view-friends-${page}\">${user.tr(title)}</a>`);\n\t\t}\n\t}\n\tconst refresh = (\n\t\t`<button class=\"button\" name=\"send\" value=\"/j view-friends${type?.trim() ? `-${type}` : ''}\" style=\"float: right\">` +\n\t\t` <i class=\"fa fa-refresh\"></i> ${user.tr('Refresh')}</button>`\n\t);\n\treturn `<div style=\"line-height:25px\">${buf.join(' / ')}${refresh}</div><hr />`;\n}\n\nexport const commands: Chat.ChatCommands = {\n\tunfriend(target) {\n\t\treturn this.parse(`/friend remove ${target}`);\n\t},\n\tfriend: 'friends',\n\tfriendslist: 'friends',\n\tfriends: {\n\t\t''(target) {\n\t\t\tif (toID(target)) {\n\t\t\t\treturn this.parse(`/friend add ${target}`);\n\t\t\t}\n\t\t\treturn this.parse(`/friends list`);\n\t\t},\n\t\tviewlist(target, room, user) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tif (!target) return this.errorReply(`Specify a user.`);\n\t\t\tif (target === user.id) return this.parse(`/friends list`);\n\t\t\treturn this.parse(`/j view-friends-viewuser-${target}`);\n\t\t},\n\t\trequest: 'add',\n\t\tasync add(target, room, user, connection) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tif (target.length > 18) {\n\t\t\t\treturn this.errorReply(this.tr`That name is too long - choose a valid name.`);\n\t\t\t}\n\t\t\tif (!target) return this.parse('/help friends');\n\t\t\tawait Friends.request(user, target as ID);\n\t\t\tthis.refreshPage('friends-sent');\n\t\t\treturn this.sendReply(`You sent a friend request to '${target}'.`);\n\t\t},\n\t\tunfriend: 'remove',\n\t\tasync remove(target, room, user) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tif (!target) return this.parse('/help friends');\n\n\t\t\tawait Friends.removeFriend(user.id, target as ID);\n\t\t\tthis.sendReply(`Removed friend '${target}'.`);\n\n\t\t\tawait Chat.Friends.updateUserCache(user);\n\t\t\tthis.refreshPage('friends-all');\n\t\t\tconst targetUser = Users.get(target);\n\t\t\tif (targetUser) await Chat.Friends.updateUserCache(targetUser);\n\t\t},\n\t\tview(target) {\n\t\t\treturn this.parse(`/join view-friends-${target}`);\n\t\t},\n\t\tlist() {\n\t\t\treturn this.parse(`/join view-friends-all`);\n\t\t},\n\t\tasync accept(target, room, user, connection) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tif (user.settings.blockFriendRequests) {\n\t\t\t\treturn this.errorReply(this.tr`You are currently blocking friend requests, and so cannot accept your own.`);\n\t\t\t}\n\t\t\tif (!target) return this.parse('/help friends');\n\t\t\tawait Friends.approveRequest(user.id, target as ID);\n\t\t\tconst targetUser = Users.get(target);\n\t\t\tsendPM(`You accepted a friend request from \"${target}\".`, user.id);\n\t\t\tthis.refreshPage('friends-received');\n\t\t\tif (targetUser) {\n\t\t\t\tsendPM(`/text ${user.name} accepted your friend request!`, targetUser.id);\n\t\t\t\tsendPM(`/uhtmlchange sent-${targetUser.id},`, targetUser.id);\n\t\t\t\tsendPM(`/uhtmlchange undo-${targetUser.id},`, targetUser.id);\n\t\t\t}\n\t\t\tawait Chat.Friends.updateUserCache(user);\n\t\t\tif (targetUser) await Chat.Friends.updateUserCache(targetUser);\n\t\t},\n\t\tdeny: 'reject',\n\t\tasync reject(target, room, user, connection) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tif (!target) return this.parse('/help friends');\n\t\t\tconst res = await Friends.removeRequest(user.id, target as ID);\n\t\t\tif (!res.changes) {\n\t\t\t\treturn this.errorReply(`You do not have a friend request pending from '${target}'.`);\n\t\t\t}\n\t\t\tthis.refreshPage('friends-received');\n\t\t\treturn sendPM(`You denied a friend request from '${target}'.`, user.id);\n\t\t},\n\t\ttoggle(target, room, user, connection) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\tconst setting = user.settings.blockFriendRequests;\n\t\t\ttarget = target.trim();\n\t\t\tif (this.meansYes(target)) {\n\t\t\t\tif (!setting) return this.errorReply(this.tr`You already are allowing friend requests.`);\n\t\t\t\tuser.settings.blockFriendRequests = false;\n\t\t\t\tthis.sendReply(this.tr`You are now allowing friend requests.`);\n\t\t\t} else if (this.meansNo(target)) {\n\t\t\t\tif (setting) return this.errorReply(this.tr`You already are blocking incoming friend requests.`);\n\t\t\t\tuser.settings.blockFriendRequests = true;\n\t\t\t\tthis.sendReply(this.tr`You are now blocking incoming friend requests.`);\n\t\t\t} else {\n\t\t\t\tif (target) this.errorReply(this.tr`Unrecognized setting.`);\n\t\t\t\tthis.sendReply(\n\t\t\t\t\tthis.tr(setting ? `You are currently blocking friend requests.` : `You are not blocking friend requests.`)\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.refreshPage('friends-settings');\n\t\t\tuser.update();\n\t\t},\n\t\tasync undorequest(target, room, user, connection) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tawait Friends.removeRequest(target as ID, user.id);\n\t\t\tthis.refreshPage('friends-sent');\n\t\t\treturn sendPM(`You removed your friend request to '${target}'.`, user.id);\n\t\t},\n\t\thidenotifs: 'viewnotifications',\n\t\thidenotifications: 'viewnotifications',\n\t\tviewnotifs: 'viewnotifications',\n\t\tviewnotifications(target, room, user, connection, cmd) {\n\t\t\t// Friends.checkCanUse(this);\n\t\t\tconst setting = user.settings.allowFriendNotifications;\n\t\t\ttarget = target.trim();\n\t\t\tif (!cmd.includes('hide') || target && this.meansYes(target)) {\n\t\t\t\tif (setting) return this.errorReply(this.tr(`You are already allowing friend notifications.`));\n\t\t\t\tuser.settings.allowFriendNotifications = true;\n\t\t\t\tthis.sendReply(this.tr(`You will now receive friend notifications.`));\n\t\t\t} else if (cmd.includes('hide') || target && this.meansNo(target)) {\n\t\t\t\tif (!setting) return this.errorReply(this.tr`You are already not receiving friend notifications.`);\n\t\t\t\tuser.settings.allowFriendNotifications = false;\n\t\t\t\tthis.sendReply(this.tr`You will not receive friend notifications.`);\n\t\t\t} else {\n\t\t\t\tif (target) this.errorReply(this.tr`Unrecognized setting.`);\n\t\t\t\tthis.sendReply(\n\t\t\t\t\tthis.tr(setting ? `You are currently allowing friend notifications.` : `Your friend notifications are disabled.`)\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.refreshPage('friends-settings');\n\t\t\tuser.update();\n\t\t},\n\t\thidelogins: 'togglelogins',\n\t\tshowlogins: 'togglelogins',\n\t\tasync togglelogins(target, room, user, connection, cmd) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\tconst setting = user.settings.hideLogins;\n\t\t\tif (cmd.includes('hide')) {\n\t\t\t\tif (setting) return this.errorReply(this.tr`You are already hiding your logins from friends.`);\n\t\t\t\tuser.settings.hideLogins = true;\n\t\t\t\tawait Chat.Friends.hideLoginData(user.id);\n\t\t\t\tthis.sendReply(`You are now hiding your login times from your friends.`);\n\t\t\t} else if (cmd.includes('show')) {\n\t\t\t\tif (!setting) return this.errorReply(this.tr`You are already allowing friends to see your login times.`);\n\t\t\t\tuser.settings.hideLogins = false;\n\t\t\t\tawait Chat.Friends.allowLoginData(user.id);\n\t\t\t\tthis.sendReply(`You are now allowing your friends to see your login times.`);\n\t\t\t} else {\n\t\t\t\treturn this.errorReply(`Invalid setting.`);\n\t\t\t}\n\t\t\tthis.refreshPage('friends-settings');\n\t\t\tuser.update();\n\t\t},\n\t\tasync listdisplay(target, room, user, connection) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tconst { public_list: setting } = await Chat.Friends.getSettings(user.id);\n\t\t\tif (this.meansYes(target)) {\n\t\t\t\tif (setting) {\n\t\t\t\t\treturn this.errorReply(this.tr`You are already allowing other people to view your friends list.`);\n\t\t\t\t}\n\t\t\t\tawait Chat.Friends.setHideList(user.id, true);\n\t\t\t\tthis.refreshPage('friends-settings');\n\t\t\t\treturn this.sendReply(this.tr`You are now allowing other people to view your friends list.`);\n\t\t\t} else if (this.meansNo(target)) {\n\t\t\t\tif (!setting) {\n\t\t\t\t\treturn this.errorReply(this.tr`You are already hiding your friends list.`);\n\t\t\t\t}\n\t\t\t\tawait Chat.Friends.setHideList(user.id, false);\n\t\t\t\tthis.refreshPage('friends-settings');\n\t\t\t\treturn this.sendReply(this.tr`You are now hiding your friends list.`);\n\t\t\t}\n\t\t\tthis.sendReply(`You are currently ${setting ? 'displaying' : 'hiding'} your friends list.`);\n\t\t},\n\t\tinvalidatecache(target, room, user) {\n\t\t\tthis.canUseConsole();\n\t\t\tfor (const curUser of Users.users.values()) {\n\t\t\t\tvoid Chat.Friends.updateUserCache(curUser);\n\t\t\t}\n\t\t\tRooms.global.notifyRooms(\n\t\t\t\t['staff', 'development'],\n\t\t\t\t`|c|${user.getIdentity()}|/log ${user.name} used /friends invalidatecache`,\n\t\t\t);\n\t\t\tthis.sendReply(`You invalidated each entry in the friends database cache.`);\n\t\t},\n\t\tsharebattles(target, room, user) {\n\t\t\tFriends.checkCanUse(this);\n\t\t\ttarget = toID(target);\n\t\t\tif (this.meansYes(target)) {\n\t\t\t\tif (user.settings.displayBattlesToFriends) {\n\t\t\t\t\treturn this.errorReply(this.tr`You are already sharing your battles with friends.`);\n\t\t\t\t}\n\t\t\t\tuser.settings.displayBattlesToFriends = true;\n\t\t\t\tthis.sendReply(`You are now allowing your friends to see your ongoing battles.`);\n\t\t\t} else if (this.meansNo(target)) {\n\t\t\t\tif (!user.settings.displayBattlesToFriends) {\n\t\t\t\t\treturn this.errorReply(this.tr`You are already not sharing your battles with friends.`);\n\t\t\t\t}\n\t\t\t\tuser.settings.displayBattlesToFriends = false;\n\t\t\t\tthis.sendReply(`You are now hiding your ongoing battles from your friends.`);\n\t\t\t} else {\n\t\t\t\tif (!target) return this.parse('/help friends sharebattles');\n\t\t\t\treturn this.errorReply(`Invalid setting '${target}'. Provide 'on' or 'off'.`);\n\t\t\t}\n\t\t\tuser.update();\n\t\t\tthis.refreshPage('friends-settings');\n\t\t},\n\t\tsharebattleshelp: [\n\t\t\t`/friends sharebattles [on|off] - Allow or disallow your friends from seeing your ongoing battles.`,\n\t\t],\n\t},\n\tfriendshelp() {\n\t\tthis.runBroadcast();\n\t\tif (this.broadcasting) {\n\t\t\treturn this.sendReplyBox([\n\t\t\t\t`<code>/friend list</code> - View current friends.`,\n\t\t\t\t`<code>/friend add [name]</code> OR <code>/friend [name]</code> - Send a friend request to [name], if you don't have them added.`,\n\t\t\t\t`<code>/friend remove [username]</code> OR <code>/unfriend [username]</code> - Unfriend the user.`,\n\t\t\t\t`<details class=\"readmore\"><summary>More commands...</summary>`,\n\t\t\t\t`<code>/friend accept [username]</code> - Accepts the friend request from [username], if it exists.`,\n\t\t\t\t`<code>/friend reject [username]</code> - Rejects the friend request from [username], if it exists.`,\n\t\t\t\t`<code>/friend toggle [off/on]</code> - Enable or disable receiving of friend requests.`,\n\t\t\t\t`<code>/friend hidenotifications</code> OR <code>hidenotifs</code> - Opts out of receiving friend notifications.`,\n\t\t\t\t`<code>/friend viewnotifications</code> OR <code>viewnotifs</code> - Opts into view friend notifications.`,\n\t\t\t\t`<code>/friend listdisplay [on/off]</code> - Opts [in/out] of letting others view your friends list.`,\n\t\t\t\t`<code>/friend viewlist [user]</code> - View the given [user]'s friend list, if they're allowing others to see.`,\n\t\t\t\t`<code>/friends sharebattles [on|off]</code> - Allow or disallow your friends from seeing your ongoing battles.</details>`,\n\t\t\t].join('<br />'));\n\t\t}\n\t\treturn this.parse('/join view-friends-help');\n\t},\n};\n\nexport const pages: Chat.PageTable = {\n\tasync friends(args, user) {\n\t\tif (!user.named) return Rooms.RETRY_AFTER_LOGIN;\n\t\tFriends.checkCanUse(this);\n\t\tconst type = args.shift();\n\t\tlet buf = '<div class=\"pad\">';\n\t\tswitch (toID(type)) {\n\t\tcase 'outgoing': case 'sent':\n\t\t\tthis.title = `[Friends] Sent`;\n\t\t\tbuf += headerButtons('sent', user);\n\t\t\tif (user.settings.blockFriendRequests) {\n\t\t\t\tbuf += `<h3>${this.tr(`You are currently blocking friend requests`)}.</h3>`;\n\t\t\t}\n\t\t\tconst { sent } = await Chat.Friends.getRequests(user);\n\t\t\tif (sent.size < 1) {\n\t\t\t\tbuf += `<strong>You have no outgoing friend requests pending.</strong><br />`;\n\t\t\t\tbuf += `<br />To add a friend, use <code>/friend add [username]</code>.`;\n\t\t\t\tbuf += `</div>`;\n\t\t\t\treturn toLink(buf);\n\t\t\t}\n\t\t\tbuf += `<h3>You have ${Chat.count(sent.size, 'friend requests')} pending${sent.size === MAX_REQUESTS ? ` (maximum reached)` : ''}.</h3>`;\n\t\t\tfor (const request of sent) {\n\t\t\t\tbuf += `<br /><div class=\"infobox\">`;\n\t\t\t\tbuf += `<strong>${request}</strong>`;\n\t\t\t\tbuf += ` <button class=\"button\" name=\"send\" value=\"/friends undorequest ${request}\">`;\n\t\t\t\tbuf += `<i class=\"fa fa-undo\"></i> ${this.tr('Undo')}</button>`;\n\t\t\t\tbuf += `</div>`;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'received': case 'incoming':\n\t\t\tthis.title = `[Friends] Received`;\n\t\t\tbuf += headerButtons('received', user);\n\t\t\tconst { received } = await Chat.Friends.getRequests(user);\n\t\t\tif (received.size < 1) {\n\t\t\t\tbuf += `<strong>You have no pending friend requests.</strong>`;\n\t\t\t\tbuf += `</div>`;\n\t\t\t\treturn toLink(buf);\n\t\t\t}\n\t\t\tbuf += `<h3>You have ${received.size} pending friend requests.</h3>`;\n\t\t\tfor (const request of received) {\n\t\t\t\tbuf += `<br /><div class=\"infobox\">`;\n\t\t\t\tbuf += `<strong>${request}</strong>`;\n\t\t\t\tbuf += ` <button class=\"button\" name=\"send\" value=\"/friends accept ${request}\">${this.tr('Accept')}</button> |`;\n\t\t\t\tbuf += ` <button class=\"button\" name=\"send\" value=\"/friends reject ${request}\">${this.tr('Deny')}</button>`;\n\t\t\t\tbuf += `</div>`;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'viewuser':\n\t\t\tconst target = toID(args.shift());\n\t\t\tif (!target) return this.errorReply(`Specify a user.`);\n\t\t\tif (target === user.id) {\n\t\t\t\treturn this.errorReply(`Use /friends list to view your own list.`);\n\t\t\t}\n\t\t\tconst { public_list: isAllowing } = await Chat.Friends.getSettings(target);\n\t\t\tif (!isAllowing) return this.errorReply(`${target}'s friends list is not public or they do not have one.`);\n\t\t\tthis.title = `[Friends List] ${target}`;\n\t\t\tbuf += await Friends.visualizePublicList(target);\n\t\t\tbreak;\n\t\tcase 'help':\n\t\t\tthis.title = `[Friends] Help`;\n\t\t\tbuf += headerButtons('help', user);\n\t\t\tbuf += `<h3>Help</h3>`;\n\t\t\tbuf += `<strong>/friend OR /friends OR /friendslist:</strong><br /><ul><li>`;\n\t\t\tbuf += [\n\t\t\t\t`<code>/friend list</code> - View current friends.`,\n\t\t\t\t`<code>/friend add [name]</code> OR <code>/friend [name]</code> - Send a friend request to [name], if you don't have them added.`,\n\t\t\t\t`<code>/friend remove [username]</code> OR <code>/unfriend [username]</code> - Unfriend the user.`,\n\t\t\t\t`<code>/friend accept [username]</code> - Accepts the friend request from [username], if it exists.`,\n\t\t\t\t`<code>/friend reject [username]</code> - Rejects the friend request from [username], if it exists.`,\n\t\t\t\t`<code>/friend toggle [off/on]</code> - Enable or disable receiving of friend requests.`,\n\t\t\t\t`<code>/friend hidenotifications</code> OR <code>hidenotifs</code> - Opts out of receiving friend notifications.`,\n\t\t\t\t`<code>/friend viewnotifications</code> OR <code>viewnotifs</code> - Opts into view friend notifications.`,\n\t\t\t\t`<code>/friend listdisplay [on/off]</code> - Opts [in/out] of letting others view your friends list.`,\n\t\t\t\t`<code>/friend viewlist [user]</code> - View the given [user]'s friend list, if they're allowing others to see.`,\n\t\t\t\t`<code>/friends sharebattles [on|off]</code> - Allow or disallow your friends from seeing your ongoing battles.`,\n\t\t\t].join('</li><li>');\n\t\t\tbuf += `</li></ul>`;\n\t\t\tbreak;\n\t\tcase 'settings':\n\t\t\tthis.title = `[Friends] Settings`;\n\t\t\tbuf += headerButtons('settings', user);\n\t\t\tbuf += `<h3>Friends Settings:</h3>`;\n\t\t\tconst settings = user.settings;\n\t\t\tconst { public_list, send_login_data } = await Chat.Friends.getSettings(user.id);\n\t\t\tbuf += `<strong>Notify me when my friends come online:</strong><br />`;\n\t\t\tbuf += `<button class=\"button${settings.allowFriendNotifications ? `` : ` disabled`}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends hidenotifs\">Disable</button> `;\n\t\t\tbuf += `<button class=\"button${settings.allowFriendNotifications ? ` disabled` : ``}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends viewnotifs\">Enable</button> <br /><br />`;\n\n\t\t\tbuf += `<strong>Receive friend requests:</strong><br />`;\n\t\t\tbuf += `<button class=\"button${settings.blockFriendRequests ? ` disabled` : ''}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends toggle off\">Disable</button> `;\n\t\t\tbuf += `<button class=\"button${settings.blockFriendRequests ? `` : ` disabled`}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends toggle on\">Enable</button> <br /><br />`;\n\n\t\t\tbuf += `<strong>Allow others to see your list:</strong><br />`;\n\t\t\tbuf += `<button class=\"button${public_list ? ` disabled` : ''}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends listdisplay yes\">Allow</button> `;\n\t\t\tbuf += `<button class=\"button${public_list ? `` : ` disabled`}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends listdisplay no\">Hide</button> <br /><br />`;\n\n\t\t\tbuf += `<strong>Allow others to see my login times</strong><br />`;\n\t\t\tbuf += `<button class=\"button${send_login_data ? ` disabled` : ''}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends hidelogins\">Disable</button> `;\n\t\t\tbuf += `<button class=\"button${send_login_data ? `` : ' disabled'}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends showlogins\">Enable</button><br /><br />`;\n\n\t\t\tbuf += `<strong>Allow friends to see my hidden battles on the spectator list:</strong><br />`;\n\t\t\tbuf += `<button class=\"button${settings.displayBattlesToFriends ? `` : ' disabled'}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends sharebattles off\">Disable</button> `;\n\t\t\tbuf += `<button class=\"button${settings.displayBattlesToFriends ? ` disabled` : ``}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/friends sharebattles on\">Enable</button> <br /><br />`;\n\n\t\t\tbuf += `<strong>Block PMs except from friends (and staff):</strong><br />`;\n\t\t\tbuf += `<button class=\"button${settings.blockPMs ? `` : ' disabled'}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/unblockpms /j view-friends-settings\">Disable</button> `;\n\t\t\tbuf += `<button class=\"button${settings.blockPMs ? ` disabled` : ``}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/blockpms friends /j view-friends-settings\">Enable</button> <br /><br />`;\n\n\t\t\tbuf += `<strong>Block challenges except from friends (and staff):</strong><br />`;\n\t\t\tbuf += `<button class=\"button${settings.blockChallenges ? `` : ' disabled'}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/unblockchallenges /j view-friends-settings\">Disable</button> `;\n\t\t\tbuf += `<button class=\"button${settings.blockChallenges ? ` disabled` : ``}\" name=\"send\" `;\n\t\t\tbuf += `value=\"/blockchallenges friends /j view-friends-settings\">Enable</button> <br /><br />`;\n\t\t\tbreak;\n\t\tcase 'spectate':\n\t\t\tthis.title = `[Friends] Spectating`;\n\t\t\tbuf += headerButtons('spectate', user);\n\t\t\tbuf += `<h3>Spectate your friends:</h3>`;\n\n\t\t\tconst toggleMessage = user.settings.displayBattlesToFriends ?\n\t\t\t\t' disallow your friends from seeing your hidden battles' :\n\t\t\t\t' allow your friends to see your hidden battles';\n\t\t\tbuf += `<i><small>Use the <a roomid=\"view-friends-settings\">settings page</a> to ${toggleMessage} on this page.</small></i><br />`;\n\t\t\tbuf += `<br />`;\n\t\t\tif (!user.friends?.size) {\n\t\t\t\tbuf += `<h3>You have no friends to spectate.</h3>`;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst friends = [];\n\t\t\tfor (const friendID of user.friends) {\n\t\t\t\tconst friend = Users.getExact(friendID);\n\t\t\t\tif (!friend) continue;\n\t\t\t\tfriends.push(friend);\n\t\t\t}\n\t\t\tif (!friends.length) {\n\t\t\t\tbuf += `<em>None of your friends are currently around to spectate.</em>`;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst battles: [User, string][] = [];\n\t\t\tfor (const friend of friends) {\n\t\t\t\tconst curBattles: [User, string][] = [...friend.inRooms]\n\t\t\t\t\t.filter(id => {\n\t\t\t\t\t\tconst battle = Rooms.get(id)?.battle;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tbattle?.playerTable[friend.id] &&\n\t\t\t\t\t\t\t(!battle.roomid.endsWith('pw') || friend.settings.displayBattlesToFriends)\n\t\t\t\t\t\t);\n\t\t\t\t\t})\n\t\t\t\t\t.map(id => [friend, id]);\n\t\t\t\tif (!curBattles.length) continue;\n\t\t\t\tbattles.push(...curBattles);\n\t\t\t}\n\t\t\tUtils.sortBy(battles, ([, id]) => -Number(id.split('-')[2]));\n\n\t\t\tif (!battles.length) {\n\t\t\t\tbuf += `<em>None of your friends are currently in a battle.</em>`;\n\t\t\t} else {\n\t\t\t\tbuf += battles.map(([friend, battle]) => {\n\t\t\t\t\t// we've already ensured the battle exists in the filter above\n\t\t\t\t\t// (and .battle only exists if it's a GameRoom, so this cast is safe)\n\t\t\t\t\tconst room = Rooms.get(battle) as GameRoom & { battle: Rooms.RoomBattle };\n\t\t\t\t\tconst format = Dex.formats.get(room.battle.format).name;\n\t\t\t\t\tconst rated = room.battle.rated ? `<small style=\"float:right\">(Rated: ${room.battle.rated})</small>` : '';\n\t\t\t\t\tconst title = room.title.includes(friend.name) ?\n\t\t\t\t\t\troom.title.replace(friend.name, `<strong>${friend.name}</strong>`) :\n\t\t\t\t\t\t(room.title + ` (with ${friend.name})`);\n\t\t\t\t\treturn `<a class=\"blocklink\" href=\"/${room.roomid}\"><small>[${format}]</small>${rated}<br /> ${title}</a>`;\n\t\t\t\t}).join('<br />');\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthis.title = `[Friends] All Friends`;\n\t\t\tbuf += headerButtons('all', user);\n\t\t\tbuf += await Friends.visualizeList(user.id);\n\t\t}\n\t\tbuf += `</div>`;\n\t\treturn toLink(buf);\n\t},\n};\n\nexport const handlers: Chat.Handlers = {\n\tonBattleStart(user) {\n\t\treturn Friends.updateSpectatorLists(user);\n\t},\n\tonBattleLeave(user, room) {\n\t\treturn Friends.updateSpectatorLists(user);\n\t},\n\tonBattleEnd(battle, winner, players) {\n\t\tfor (const id of players) {\n\t\t\tconst user = Users.get(id);\n\t\t\tif (!user) continue;\n\t\t\tFriends.updateSpectatorLists(user);\n\t\t}\n\t},\n\tonDisconnect(user) {\n\t\tvoid Chat.Friends.writeLogin(user.id);\n\t},\n};\n\nexport const loginfilter: Chat.LoginFilter = user => {\n\tif (!Config.usesqlitefriends || !Users.globalAuth.atLeast(user, Config.usesqlitefriends)) {\n\t\treturn;\n\t}\n\n\t// notify users of pending requests\n\tvoid Friends.notifyPending(user);\n\n\t// (quietly) notify their friends (that have opted in) that they are online\n\tvoid Friends.notifyConnection(user);\n\t// write login time\n\tvoid Chat.Friends.writeLogin(user.id);\n\n\tvoid Chat.Friends.updateUserCache(user);\n};\n"], | |
"mappings": "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", | |
"names": [] | |
} | |