inital member list support

This commit is contained in:
MathMan05 2024-09-30 15:47:52 -05:00
parent b2a31d7993
commit a1902a912f
8 changed files with 501 additions and 288 deletions

View file

@ -377,7 +377,10 @@ class Channel extends SnowFlake{
if(member.isAdmin()){ if(member.isAdmin()){
return true; return true;
} }
for(const thing of member.roles){ const roles=new Set(member.roles);
const everyone=this.guild.roles[this.guild.roles.length-1];
roles.add(everyone)
for(const thing of roles){
const premission = this.permission_overwrites.get(thing.id); const premission = this.permission_overwrites.get(thing.id);
if(premission){ if(premission){
const perm = premission.getPermission(name); const perm = premission.getPermission(name);
@ -834,6 +837,7 @@ class Channel extends SnowFlake{
Channel.regenLoadingMessages(); Channel.regenLoadingMessages();
loading.classList.add("loading"); loading.classList.add("loading");
this.rendertyping(); this.rendertyping();
this.localuser.getSidePannel();
await this.putmessages(); await this.putmessages();
await prom; await prom;
if(id !== Channel.genid){ if(id !== Channel.genid){

View file

@ -34,6 +34,7 @@ class Guild extends SnowFlake{
html!: HTMLElement; html!: HTMLElement;
emojis!: emojijson[]; emojis!: emojijson[];
large!: boolean; large!: boolean;
members=new Set<Member>();
static contextmenu = new Contextmenu<Guild, undefined>("guild menu"); static contextmenu = new Contextmenu<Guild, undefined>("guild menu");
static setupcontextmenu(){ static setupcontextmenu(){
Guild.contextmenu.addbutton("Copy Guild id", function(this: Guild){ Guild.contextmenu.addbutton("Copy Guild id", function(this: Guild){

View file

@ -56,6 +56,8 @@
<span id="channelname">Channel name</span> <span id="channelname">Channel name</span>
<span id="channelTopic" hidden>Channel topic</span> <span id="channelTopic" hidden>Channel topic</span>
</div> </div>
<div class="flexltr">
<div class="flexttb">
<div id="channelw"> <div id="channelw">
<div id="loadingdiv"> <div id="loadingdiv">
</div> </div>
@ -76,6 +78,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flexttb" id="sideDiv">
</div>
</div>
</div>
</div> </div>
</body> </body>

View file

@ -145,21 +145,22 @@ pronouns: string;
badge_ids: string[]; badge_ids: string[];
}; };
type memberjson = { type memberjson = {
index?: number; index?: number;
id: string; id: string;
user: userjson | null; user: userjson | null;
guild_id: string; guild_id: string;
guild: { guild: {
id: string; id: string;
} | null; } | null;
nick?: string; presence?:presencejson
roles: string[]; nick?: string;
joined_at: string; roles: string[];
premium_since: string; joined_at: string;
deaf: boolean; premium_since: string;
mute: boolean; deaf: boolean;
pending: boolean; mute: boolean;
last_message_id?: boolean; //What??? pending: boolean;
last_message_id?: boolean; //What???
}; };
type emojijson = { type emojijson = {
name: string; name: string;
@ -257,18 +258,18 @@ default_thread_rate_limit_per_user: number;
position: number; position: number;
}; };
type rolesjson = { type rolesjson = {
id: string; id: string;
guild_id: string; guild_id: string;
color: number; color: number;
hoist: boolean; hoist: boolean;
managed: boolean; managed: boolean;
mentionable: boolean; mentionable: boolean;
name: string; name: string;
permissions: string; permissions: string;
position: number; position: number;
icon: string; icon: string;
unicode_emoji: string; unicode_emoji: string;
flags: number; flags: number;
}; };
type dirrectjson = { type dirrectjson = {
id: string; id: string;
@ -392,20 +393,20 @@ t: "MESSAGE_CREATE";
}; };
type wsjson = type wsjson =
| { | {
op: 0; op: 0;
d: any; d: any;
s: number; s: number;
t: t:
| "TYPING_START" | "TYPING_START"
| "USER_UPDATE" | "USER_UPDATE"
| "CHANNEL_UPDATE" | "CHANNEL_UPDATE"
| "CHANNEL_CREATE" | "CHANNEL_CREATE"
| "CHANNEL_DELETE" | "CHANNEL_DELETE"
| "GUILD_DELETE" | "GUILD_DELETE"
| "GUILD_CREATE" | "GUILD_CREATE"
| "MESSAGE_REACTION_REMOVE_ALL" | "MESSAGE_REACTION_REMOVE_ALL"
| "MESSAGE_REACTION_REMOVE_EMOJI"; | "MESSAGE_REACTION_REMOVE_EMOJI";
} }
| { | {
op: 0; op: 0;
t: "GUILD_MEMBERS_CHUNK"; t: "GUILD_MEMBERS_CHUNK";
@ -469,7 +470,7 @@ guild_id: string;
emoji: emojijson; emoji: emojijson;
}; };
s: 3; s: 3;
}; }|memberlistupdatejson;
type memberChunk = { type memberChunk = {
guild_id: string; guild_id: string;
nonce: string; nonce: string;
@ -479,6 +480,39 @@ chunk_index: number;
chunk_count: number; chunk_count: number;
not_found: string[]; not_found: string[];
}; };
type memberlistupdatejson={
op: 0,
s: number,
t: "GUILD_MEMBER_LIST_UPDATE",
d: {
ops: [
{
items:({
group:{
count:number,
id:string
}
}|{
member:memberjson
})[]
op: "SYNC",
range: [
number,
number
]
}
],
online_count: number,
member_count: number,
id: string,
guild_id: string,
groups: {
count: number,
id: string
}[]
}
}
export{ export{
readyjson, readyjson,
dirrectjson, dirrectjson,
@ -498,4 +532,5 @@ export{
messageCreateJson, messageCreateJson,
memberChunk, memberChunk,
invitejson, invitejson,
memberlistupdatejson
}; };

View file

@ -10,6 +10,7 @@ import{
guildjson, guildjson,
mainuserjson, mainuserjson,
memberjson, memberjson,
memberlistupdatejson,
messageCreateJson, messageCreateJson,
presencejson, presencejson,
readyjson, readyjson,
@ -20,6 +21,7 @@ import{ Member }from"./member.js";
import{ Form, FormError, Options, Settings }from"./settings.js"; import{ Form, FormError, Options, Settings }from"./settings.js";
import{ MarkDown }from"./markdown.js"; import{ MarkDown }from"./markdown.js";
import { Bot } from "./bot.js"; import { Bot } from "./bot.js";
import { Role } from "./role.js";
const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]); const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]);
@ -479,7 +481,13 @@ class Localuser{
case"GUILD_MEMBERS_CHUNK": case"GUILD_MEMBERS_CHUNK":
this.gotChunk(temp.d); this.gotChunk(temp.d);
break; break;
case"GUILD_MEMBER_LIST_UPDATE":
{
this.memberListUpdate(temp)
break;
} }
}
}else if(temp.op === 10){ }else if(temp.op === 10){
if(!this.ws)return; if(!this.ws)return;
console.log("heartbeat down"); console.log("heartbeat down");
@ -518,6 +526,108 @@ class Localuser{
} }
return channel; // Add this line to return the 'channel' variable return channel; // Add this line to return the 'channel' variable
} }
async memberListUpdate(list:memberlistupdatejson){
const div=document.getElementById("sideDiv") as HTMLDivElement;
div.innerHTML="";
const counts=new Map<string,number>();
const guild=this.lookingguild;
if(!guild) return;
const channel=this.channelfocus;
if(!channel) return;
for(const thing of list.d.ops[0].items){
if("member" in thing){
await Member.new(thing.member,guild);
}else{
counts.set(thing.group.id,thing.group.count);
}
}
const elms:Map<Role|"offline"|"online",Member[]>=new Map([["offline",[]],["online",[]]]);
for(const role of guild.roles){
console.log(guild.roles);
if(role.hoist){
elms.set(role,[]);
}
}
const members=new Set(guild.members);
members.forEach((member)=>{
if(!channel.hasPermission("VIEW_CHANNEL",member)){
members.delete(member);
console.log(member)
return;
}
})
for(const [role, list] of elms){
members.forEach((member)=>{
if(role === "offline"){
if(member.user.status === "offline"){
list.push(member);
members.delete(member);
}
return;
}
if(role !== "online"&&member.hasRole(role.id)){
list.push(member);
members.delete(member);
}
});
if(!list.length) continue;
list.sort((a,b)=>{
return (a.name.toLowerCase()>b.name.toLowerCase())?1:-1;
});
}
const online=[...members];
online.sort((a,b)=>{
return (a.name.toLowerCase()>b.name.toLowerCase())?1:-1;
});
elms.set("online",online);
for(const [role, list] of elms){
if(!list.length) continue;
const category=document.createElement("div");
category.classList.add("memberList");
let title=document.createElement("h3");
if(role==="offline"){
title.textContent="Offline";
category.classList.add("offline");
}else if(role==="online"){
title.textContent="Online";
}else{
title.textContent=role.name;
}
category.append(title);
const membershtml=document.createElement("div");
membershtml.classList.add("flexttb");
for(const member of list){
const memberdiv=document.createElement("div");
const pfp=member.user.buildpfp();
const username=document.createElement("span");
username.textContent=member.name;
member.bind(username)
member.user.bind(memberdiv,member.guild,false);
memberdiv.append(pfp,username);
memberdiv.classList.add("flexltr");
membershtml.append(memberdiv);
}
category.append(membershtml);
div.prepend(category);
}
console.log(elms);
}
async getSidePannel(){
if(this.ws&&this.channelfocus){
this.ws.send(JSON.stringify({
d:{
channels:{[this.channelfocus.id]:[[0,99]]},
guild_id:this.channelfocus.guild.id
},
op:14
}))
}else{
console.log("false? :3")
}
}
gotoid: string | undefined; gotoid: string | undefined;
async goToChannel(id: string){ async goToChannel(id: string){
const channel = this.channelids.get(id); const channel = this.channelids.get(id);

View file

@ -11,9 +11,8 @@ class Member extends SnowFlake{
user: User; user: User;
roles: Role[] = []; roles: Role[] = [];
nick!: string; nick!: string;
[key: string]: any;
private constructor(memberjson: memberjson, owner: Guild){ private constructor(memberjson: memberjson, owner: Guild){
super(memberjson.id); super(memberjson.id);
this.owner = owner; this.owner = owner;
if(this.localuser.userMap.has(memberjson.id)){ if(this.localuser.userMap.has(memberjson.id)){
@ -23,9 +22,11 @@ private constructor(memberjson: memberjson, owner: Guild){
}else{ }else{
throw new Error("Missing user object of this member"); throw new Error("Missing user object of this member");
} }
if(this.localuser.userMap.has(this?.id)){
this.user = this.localuser.userMap.get(this?.id) as User;
}
for(const key of Object.keys(memberjson)){ for(const key of Object.keys(memberjson)){
if(key === "guild" || key === "owner"){ if(key === "guild" || key === "owner" || key === "user"){
continue; continue;
} }
@ -37,28 +38,30 @@ private constructor(memberjson: memberjson, owner: Guild){
} }
continue; continue;
} }
if(key === "presence"){
this.getPresence(memberjson.presence);
continue;
}
(this as any)[key] = (memberjson as any)[key]; (this as any)[key] = (memberjson as any)[key];
} }
if(this.localuser.userMap.has(this?.id)){
this.user = this.localuser.userMap.get(this?.id) as User;
}
this.roles.sort((a, b)=>{ this.roles.sort((a, b)=>{
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b); return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
}); });
} }
get guild(){ get guild(){
return this.owner; return this.owner;
} }
get localuser(){ get localuser(){
return this.guild.localuser; return this.guild.localuser;
} }
get info(){ get info(){
return this.owner.info; return this.owner.info;
} }
static async new( static async new(
memberjson: memberjson, memberjson: memberjson,
owner: Guild owner: Guild
): Promise<Member | undefined>{ ): Promise<Member | undefined>{
let user: User; let user: User;
if(owner.localuser.userMap.has(memberjson.id)){ if(owner.localuser.userMap.has(memberjson.id)){
user = owner.localuser.userMap.get(memberjson.id) as User; user = owner.localuser.userMap.get(memberjson.id) as User;
@ -72,22 +75,27 @@ static async new(
if(memb === undefined){ if(memb === undefined){
memb = new Member(memberjson, owner); memb = new Member(memberjson, owner);
user.members.set(owner, memb); user.members.set(owner, memb);
owner.members.add(memb);
return memb; return memb;
}else if(memb instanceof Promise){ }else if(memb instanceof Promise){
return await memb; //I should do something else, though for now this is "good enough" return await memb; //I should do something else, though for now this is "good enough"
}else{ }else{
if(memberjson.presence){
memb.getPresence(memberjson.presence);
}
return memb; return memb;
} }
}else{ }else{
const memb = new Member(memberjson, owner); const memb = new Member(memberjson, owner);
user.members.set(owner, memb); user.members.set(owner, memb);
owner.members.add(memb);
return memb; return memb;
} }
} }
static async resolveMember( static async resolveMember(
user: User, user: User,
guild: Guild guild: Guild
): Promise<Member | undefined>{ ): Promise<Member | undefined>{
const maybe = user.members.get(guild); const maybe = user.members.get(guild);
if(!user.members.has(guild)){ if(!user.members.has(guild)){
const membpromise = guild.localuser.resolvemember(user.id, guild.id); const membpromise = guild.localuser.resolvemember(user.id, guild.id);
@ -111,14 +119,14 @@ static async resolveMember(
}else{ }else{
return maybe; return maybe;
} }
} }
public getPresence(presence: presencejson | undefined){ public getPresence(presence: presencejson | undefined){
this.user.getPresence(presence); this.user.getPresence(presence);
} }
/** /**
* @todo * @todo
*/ */
highInfo(){ highInfo(){
fetch( fetch(
this.info.api + this.info.api +
"/users/" + "/users/" +
@ -127,8 +135,8 @@ highInfo(){
this.guild.id, this.guild.id,
{ headers: this.guild.headers } { headers: this.guild.headers }
); );
} }
hasRole(ID: string){ hasRole(ID: string){
console.log(this.roles, ID); console.log(this.roles, ID);
for(const thing of this.roles){ for(const thing of this.roles){
if(thing.id === ID){ if(thing.id === ID){
@ -136,8 +144,8 @@ hasRole(ID: string){
} }
} }
return false; return false;
} }
getColor(){ getColor(){
for(const thing of this.roles){ for(const thing of this.roles){
const color = thing.getColor(); const color = thing.getColor();
if(color){ if(color){
@ -145,16 +153,16 @@ getColor(){
} }
} }
return""; return"";
} }
isAdmin(){ isAdmin(){
for(const role of this.roles){ for(const role of this.roles){
if(role.permissions.getPermission("ADMINISTRATOR")){ if(role.permissions.getPermission("ADMINISTRATOR")){
return true; return true;
} }
} }
return this.guild.properties.owner_id === this.user.id; return this.guild.properties.owner_id === this.user.id;
} }
bind(html: HTMLElement){ bind(html: HTMLElement){
if(html.tagName === "SPAN"){ if(html.tagName === "SPAN"){
if(!this){ if(!this){
return; return;
@ -168,14 +176,14 @@ bind(html: HTMLElement){
} }
//this.profileclick(html); //this.profileclick(html);
} }
profileclick(/* html: HTMLElement */){ profileclick(/* html: HTMLElement */){
//to be implemented //to be implemented
} }
get name(){ get name(){
return this.nick || this.user.username; return this.nick || this.user.username;
} }
kick(){ kick(){
let reason = ""; let reason = "";
const menu = new Dialog([ const menu = new Dialog([
"vdiv", "vdiv",
@ -199,16 +207,16 @@ kick(){
], ],
]); ]);
menu.show(); menu.show();
} }
kickAPI(reason: string){ kickAPI(reason: string){
const headers = structuredClone(this.guild.headers); const headers = structuredClone(this.guild.headers);
(headers as any)["x-audit-log-reason"] = reason; (headers as any)["x-audit-log-reason"] = reason;
fetch(`${this.info.api}/guilds/${this.guild.id}/members/${this.id}`, { fetch(`${this.info.api}/guilds/${this.guild.id}/members/${this.id}`, {
method: "DELETE", method: "DELETE",
headers, headers,
}); });
} }
ban(){ ban(){
let reason = ""; let reason = "";
const menu = new Dialog([ const menu = new Dialog([
"vdiv", "vdiv",
@ -232,16 +240,16 @@ ban(){
], ],
]); ]);
menu.show(); menu.show();
} }
banAPI(reason: string){ banAPI(reason: string){
const headers = structuredClone(this.guild.headers); const headers = structuredClone(this.guild.headers);
(headers as any)["x-audit-log-reason"] = reason; (headers as any)["x-audit-log-reason"] = reason;
fetch(`${this.info.api}/guilds/${this.guild.id}/bans/${this.id}`, { fetch(`${this.info.api}/guilds/${this.guild.id}/bans/${this.id}`, {
method: "PUT", method: "PUT",
headers, headers,
}); });
} }
hasPermission(name: string): boolean{ hasPermission(name: string): boolean{
if(this.isAdmin()){ if(this.isAdmin()){
return true; return true;
} }
@ -251,6 +259,6 @@ hasPermission(name: string): boolean{
} }
} }
return false; return false;
} }
} }
export{ Member }; export{ Member };

View file

@ -1379,6 +1379,7 @@ span {
width: 100%; width: 100%;
height: 100dvh; height: 100dvh;
align-content: space-around; align-content: space-around;
align-items: stretch;
} }
.userflex{ .userflex{
display:flex; display:flex;
@ -2219,3 +2220,51 @@ form div{
.mentionMD:hover{ .mentionMD:hover{
background:color-mix(in srgb,var(--mention-md-bg),white 10%); background:color-mix(in srgb,var(--mention-md-bg),white 10%);
} }
#sideDiv{
flex-grow:0;
flex-shrink:0;
width:2.5in;
box-shadow: -.02in 0 .04in black;
&:empty{
width:0in;
}
;
align-items: stretch;
/* overflow-x: hidden; */
overflow-y: unset;
scrollbar-width: none;
}
.memberList{
padding:.05in;
>div{
width: 100%;
>div{
width:100%;
padding:.04in .02in;
margin: .01in 0.0in;
border-radius:.1in;
&:hover{
background:var(--message-bg-hover);
}
flex-shrink: 1;
cursor: pointer;
box-sizing:border-box;
gap:.05in;
align-items: center;
}
img{
width:40px;
height:40px;
}
box-sizing: border-box;
}
}
.offline{
h3{
color:var(--primary-text)
}
>div{
opacity:.4;
}
}

View file

@ -27,7 +27,7 @@ class User extends SnowFlake{
badge_ids!: string[]; badge_ids!: string[];
members: WeakMap<Guild, Member | undefined | Promise<Member | undefined>> = members: WeakMap<Guild, Member | undefined | Promise<Member | undefined>> =
new WeakMap(); new WeakMap();
private status!: string; status!: string;
resolving: false | Promise<any> = false; resolving: false | Promise<any> = false;
constructor(userjson: userjson, owner: Localuser, dontclone = false){ constructor(userjson: userjson, owner: Localuser, dontclone = false){