import type { Category , PermissionTuple , Role , Server as ServerI , SystemMessageChannels , } from "revolt-api/types/Servers" ; import type { RemoveServerField , Route } from "../api/routes" ; import type { Attachment } from "revolt-api/types/Autumn" ; import { makeAutoObservable , action , runInAction , computed } from "mobx" ; import isEqual from "lodash.isequal" ; import { Nullable , toNullable } from "../util/null" ; import { U32_MAX } from "../api/permissions" ; import Collection from "./Collection" ; import { User } from "./Users" ; import { Client , FileArgs } from ".." ; import { decodeTime } from "ulid" ; import { INotificationChecker } from "../util/Unreads" ; export class Server { client : Client ; _id : string ; owner : string ; name : string ; description : Nullable < string > = null ; channel_ids : string [ ] = [ ] ; categories : Nullable < Category [ ] > = null ; system_messages : Nullable < SystemMessageChannels > = null ; roles : Nullable < { [ key ] : Role ; } > = null ; default_permissions : PermissionTuple ; icon : Nullable < Attachment > = null ; banner : Nullable < Attachment > = null ; nsfw : Nullable < boolean > = null ; flags : Nullable < number > = null ; get channels ( ) { return this . channel_ids . map ( ( x ) => this . client . channels . get ( x ) ) ; } /**
     * Get timestamp when this server was created.
     */ get createdAt ( ) { return decodeTime ( this . _id ) ; } /**
     * Absolute pathname to this server in the client.
     */ get path ( ) { return `/server/${ this . _id }` ; } /**
     * Get URL to this server.
     */ get url ( ) { return this . client . configuration ?. app + this . path ; } @ computed isUnread ( permit ? ) { if ( permit ?. isMuted ( this ) ) return false ; return this . channels . find ( ( channel ) => ! permit ?. isMuted ( channel ) && channel ?. unread ) ; } @ computed getMentions ( permit ? ) { if ( permit ?. isMuted ( this ) ) return [ ] ; const arr = this . channels . filter ( channel => ! permit ?. isMuted ( channel ) ) . map ( ( channel ) => channel ?. mentions ) as string [ ] [ ] ; return ( [ ] as string [ ] ) . concat ( ... arr ) ; } constructor ( client , data ) { this . client = client ; this . _id = data . _id ; this . owner = data . owner ; this . name = data . name ; this . description = toNullable ( data . description ) ; this . channel_ids = data . channels ; this . categories = toNullable ( data . categories ) ; this . system_messages = toNullable ( data . system_messages ) ; this . roles = toNullable ( data . roles ) ; this . default_permissions = data . default_permissions ; this . icon = toNullable ( data . icon ) ; this . banner = toNullable ( data . banner ) ; this . nsfw = toNullable ( data . nsfw ) ; this . flags = toNullable ( data . flags ) ; makeAutoObservable ( this , { _id : false , client : false , } ) ; } @ action update ( data , clear ? ) { const apply = ( key , target ? ) => { if ( typeof data [ key ] !== "undefined" && ! isEqual ( this [ target ?? key ] , data [ key ] ) ) { this [ target ?? key ] = data [ key ] ; } } ; switch ( clear ) { case "Banner" : this . banner = null ; break ; case "Description" : this . description = null ; break ; case "Icon" : this . icon = null ; break ; } apply ( "owner" ) ; apply ( "name" ) ; apply ( "description" ) ; apply ( "channels" , "channel_ids" ) ; apply ( "categories" ) ; apply ( "system_messages" ) ; apply ( "roles" ) ; apply ( "default_permissions" ) ; apply ( "icon" ) ; apply ( "banner" ) ; apply ( "nsfw" ) ; apply ( "flags" ) ; } param data returns async createChannel ( data ) { return await this . client . req ( "POST" , `/servers/${ this . _id }/channels` as "/servers/id/channels" , data ) ; } param data async edit ( data ) { return await this . client . req ( "PATCH" , `/servers/${ this . _id }` as "/servers/id" , data ) ; } /**
     * Delete a guild
     */ async delete ( avoidReq ? ) { if ( ! avoidReq ) await this . client . req ( "DELETE" , `/servers/${ this . _id }` as "/servers/id" ) ; runInAction ( ( ) => { this . client . servers . delete ( this . _id ) ; } ) ; } /**
     * Mark a server as read
     */ async ack ( ) { return await this . client . req ( "PUT" , `/servers/${ this . _id }/ack` as "/servers/id/ack" ) ; } param user_id async banUser ( user_id , data ) { return await this . client . req ( "PUT" , `/servers/${ this . _id }/bans/${ user_id }` as "/servers/id/bans/id" , data ) ; } param user_id async unbanUser ( user_id ) { return await this . client . req ( "DELETE" , `/servers/${ this . _id }/bans/${ user_id }` as "/servers/id/bans/id" ) ; } returns async fetchInvites ( ) { return await this . client . req ( "GET" , `/servers/${ this . _id }/invites` as "/servers/id/invites" ) ; } returns async fetchBans ( ) { return await this . client . req ( "GET" , `/servers/${ this . _id }/bans` as "/servers/id/bans" ) ; } param role_id param permissions async setPermissions ( role_id = "default" , permissions ? ) { return await this . client . req ( "PUT" , `/servers/${ this . _id }/permissions/${ role_id }` as "/servers/id/permissions/id" , { permissions } ) ; } param name async createRole ( name ) { return await this . client . req ( "POST" , `/servers/${ this . _id }/roles` as "/servers/id/roles" , { name } ) ; } param role_id param data async editRole ( role_id , data ) { return await this . client . req ( "PATCH" , `/servers/${ this . _id }/roles/${ role_id }` as "/servers/id/roles/id" , data ) ; } param role_id async deleteRole ( role_id ) { return await this . client . req ( "DELETE" , `/servers/${ this . _id }/roles/${ role_id }` as "/servers/id/roles/id" ) ; } param user returns async fetchMember ( user ) { const user_id = typeof user === "string" ? user : user . _id ; const existing = this . client . members . getKey ( { server : this . _id , user : user_id , } ) ; if ( existing ) return existing ; const member = await this . client . req ( "GET" , `/servers/${ this . _id }/members/${ user_id }` as "/servers/id/members/id" ) ; return this . client . members . createObj ( member ) ; } /**
     * Optimised member fetch route.
     * ! OPTIMISATION
     */ async syncMembers ( skipOffline ? ) { const data = await this . client . req ( "GET" , `/servers/${ this . _id }/members` as "/servers/id/members" ) ; data . users . sort ( ( a , b ) => b . _id . localeCompare ( a . _id ) ) ; data . members . sort ( ( a , b ) => b . _id . user . localeCompare ( a . _id . user ) ) ; runInAction ( ( ) => { for ( let i = 0 ; i < data . users . length ; i ++ ) { if ( data . users [ i ] . online ) { this . client . users . createObj ( data . users [ i ] ) ; this . client . members . createObj ( data . members [ i ] ) ; } } if ( skipOffline ) return ; let j = 0 ; const batch = ( ) => { const offset = j * 100 ; runInAction ( ( ) => { for ( let i = offset ; i < data . users . length && i < offset + 100 ; i ++ ) { if ( ! data . users [ i ] . online ) { this . client . users . createObj ( data . users [ i ] ) ; this . client . members . createObj ( data . members [ i ] ) ; } } } ) ; if ( offset < data . users . length ) { j ++ ; setTimeout ( batch , 0 ) ; } } ; setTimeout ( batch , 0 ) ; } ) ; } returns async fetchMembers ( ) { const data = await this . client . req ( "GET" , `/servers/${ this . _id }/members` as "/servers/id/members" ) ; return runInAction ( ( ) => { return { members : data . members . map ( this . client . members . createObj ) , users : data . users . map ( this . client . users . createObj ) , } ; } ) ; } @ computed generateIconURL ( ... args ) { return this . client . generateFileURL ( this . icon ?? undefined , ... args ) ; } @ computed generateBannerURL ( ... args ) { return this . client . generateFileURL ( this . banner ?? undefined , ... args ) ; } @ computed get permission ( ) { if ( this . owner === this . client . user ?. _id ) { return U32_MAX ; } else { const member = this . client . members . getKey ( { user : this . client . user ! . _id , server : this . _id , } ) ?? { roles : null } ; if ( ! member ) return 0 ; let perm = this . default_permissions [ 0 ] >>> 0 ; if ( member . roles ) { for ( const role of member . roles ) { perm |= ( this . roles ?. [ role ] . permissions [ 0 ] ?? 0 ) >>> 0 ; } } return perm ; } } } export default class Servers extends Collection < string , Server > { constructor ( client ) { super ( client ) ; this . createObj = this . createObj . bind ( this ) ; } @ action $get ( id , data ? ) { const server = this . get ( id ) ! ; if ( data ) server . update ( data ) ; return server ; } param id returns async fetch ( id , data ? ) { if ( this . has ( id ) ) return this . $get ( id , data ) ; const res = data ?? ( await this . client . req ( "GET" , `/servers/${ id }` as "/servers/id" ) ) ; return runInAction ( async ( ) => { for ( const channel of res . channels ) { try { await this . client . channels . fetch ( channel ) ; } catch ( err ) { } } return this . createObj ( res ) ; } ) ; } param data returns createObj ( data ) { if ( this . has ( data . _id ) ) return this . $get ( data . _id , data ) ; const server = new Server ( this . client , data ) ; runInAction ( ( ) => { this . set ( data . _id , server ) ; } ) ; return server ; } param data returns async createServer ( data ) { const server = await this . client . req ( "POST" , `/servers/create` , data ) ; return this . fetch ( server . _id , server ) ; } }