update instancejson more
MY HEAD HURTS NOW ;( . . more work Finish rewrite all finished
This commit is contained in:
parent
3a61417de2
commit
758bd7cc7a
113 changed files with 12520 additions and 20427 deletions
724
src/webpage/guild.ts
Normal file
724
src/webpage/guild.ts
Normal file
|
@ -0,0 +1,724 @@
|
|||
import { Channel } from "./channel.js";
|
||||
import { Localuser } from "./localuser.js";
|
||||
import { Contextmenu } from "./contextmenu.js";
|
||||
import { Role, RoleList } from "./role.js";
|
||||
import { Dialog } from "./dialog.js";
|
||||
import { Member } from "./member.js";
|
||||
import { Settings } from "./settings.js";
|
||||
import { Permissions } from "./permissions.js";
|
||||
import { SnowFlake } from "./snowflake.js";
|
||||
import {
|
||||
channeljson,
|
||||
guildjson,
|
||||
emojijson,
|
||||
memberjson,
|
||||
invitejson,
|
||||
} from "./jsontypes.js";
|
||||
import { User } from "./user.js";
|
||||
|
||||
class Guild extends SnowFlake {
|
||||
owner!: Localuser;
|
||||
headers!: Localuser["headers"];
|
||||
channels!: Channel[];
|
||||
properties!: guildjson["properties"];
|
||||
member_count!: number;
|
||||
roles!: Role[];
|
||||
roleids!: Map<string, Role>;
|
||||
prevchannel: Channel | undefined;
|
||||
banner!: string;
|
||||
message_notifications!: number;
|
||||
headchannels!: Channel[];
|
||||
position!: number;
|
||||
parent_id!: string;
|
||||
member!: Member;
|
||||
html!: HTMLElement;
|
||||
emojis!: emojijson[];
|
||||
large!: boolean;
|
||||
static contextmenu = new Contextmenu<Guild, undefined>("guild menu");
|
||||
static setupcontextmenu() {
|
||||
Guild.contextmenu.addbutton("Copy Guild id", function (this: Guild) {
|
||||
navigator.clipboard.writeText(this.id);
|
||||
});
|
||||
|
||||
Guild.contextmenu.addbutton("Mark as read", function (this: Guild) {
|
||||
this.markAsRead();
|
||||
});
|
||||
|
||||
Guild.contextmenu.addbutton("Notifications", function (this: Guild) {
|
||||
this.setnotifcation();
|
||||
});
|
||||
|
||||
Guild.contextmenu.addbutton(
|
||||
"Leave guild",
|
||||
function (this: Guild) {
|
||||
this.confirmleave();
|
||||
},
|
||||
null,
|
||||
function (_) {
|
||||
return this.properties.owner_id !== this.member.user.id;
|
||||
}
|
||||
);
|
||||
|
||||
Guild.contextmenu.addbutton(
|
||||
"Delete guild",
|
||||
function (this: Guild) {
|
||||
this.confirmDelete();
|
||||
},
|
||||
null,
|
||||
function (_) {
|
||||
return this.properties.owner_id === this.member.user.id;
|
||||
}
|
||||
);
|
||||
|
||||
Guild.contextmenu.addbutton(
|
||||
"Create invite",
|
||||
function (this: Guild) {},
|
||||
null,
|
||||
(_) => true,
|
||||
(_) => false
|
||||
);
|
||||
Guild.contextmenu.addbutton("Settings", function (this: Guild) {
|
||||
this.generateSettings();
|
||||
});
|
||||
/* -----things left for later-----
|
||||
guild.contextmenu.addbutton("Leave Guild",function(){
|
||||
console.log(this)
|
||||
this.deleteChannel();
|
||||
},null,_=>{return thisuser.isAdmin()})
|
||||
|
||||
guild.contextmenu.addbutton("Mute Guild",function(){
|
||||
editchannelf(this);
|
||||
},null,_=>{return thisuser.isAdmin()})
|
||||
*/
|
||||
}
|
||||
generateSettings() {
|
||||
const settings = new Settings("Settings for " + this.properties.name);
|
||||
{
|
||||
const overview = settings.addButton("Overview");
|
||||
const form = overview.addForm("", (_) => {}, {
|
||||
headers: this.headers,
|
||||
traditionalSubmit: true,
|
||||
fetchURL: this.info.api + "/guilds/" + this.id,
|
||||
method: "PATCH",
|
||||
});
|
||||
form.addTextInput("Name:", "name", { initText: this.properties.name });
|
||||
form.addMDInput("Description:", "description", {
|
||||
initText: this.properties.description,
|
||||
});
|
||||
form.addFileInput("Banner:", "banner", { clear: true });
|
||||
form.addFileInput("Icon:", "icon", { clear: true });
|
||||
let region = this.properties.region;
|
||||
if (!region) {
|
||||
region = "";
|
||||
}
|
||||
form.addTextInput("Region:", "region", { initText: region });
|
||||
}
|
||||
const s1 = settings.addButton("roles");
|
||||
const permlist: [Role, Permissions][] = [];
|
||||
for (const thing of this.roles) {
|
||||
permlist.push([thing, thing.permissions]);
|
||||
}
|
||||
s1.options.push(
|
||||
new RoleList(permlist, this, this.updateRolePermissions.bind(this))
|
||||
);
|
||||
settings.show();
|
||||
}
|
||||
constructor(
|
||||
json: guildjson | -1,
|
||||
owner: Localuser,
|
||||
member: memberjson | User | null
|
||||
) {
|
||||
if (json === -1 || member === null) {
|
||||
super("@me");
|
||||
return;
|
||||
}
|
||||
if (json.stickers.length) {
|
||||
console.log(json.stickers, ":3");
|
||||
}
|
||||
super(json.id);
|
||||
this.large = json.large;
|
||||
this.member_count = json.member_count;
|
||||
this.emojis = json.emojis;
|
||||
this.owner = owner;
|
||||
this.headers = this.owner.headers;
|
||||
this.channels = [];
|
||||
this.properties = json.properties;
|
||||
this.roles = [];
|
||||
this.roleids = new Map();
|
||||
|
||||
this.message_notifications = 0;
|
||||
for (const roley of json.roles) {
|
||||
const roleh = new Role(roley, this);
|
||||
this.roles.push(roleh);
|
||||
this.roleids.set(roleh.id, roleh);
|
||||
}
|
||||
if (member instanceof User) {
|
||||
Member.resolveMember(member, this).then((_) => {
|
||||
if (_) {
|
||||
this.member = _;
|
||||
} else {
|
||||
console.error("Member was unable to resolve");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Member.new(member, this).then((_) => {
|
||||
if (_) {
|
||||
this.member = _;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.perminfo ??= { channels: {} };
|
||||
for (const thing of json.channels) {
|
||||
const temp = new Channel(thing, this);
|
||||
this.channels.push(temp);
|
||||
this.localuser.channelids.set(temp.id, temp);
|
||||
}
|
||||
this.headchannels = [];
|
||||
for (const thing of this.channels) {
|
||||
const parent = thing.resolveparent(this);
|
||||
if (!parent) {
|
||||
this.headchannels.push(thing);
|
||||
}
|
||||
}
|
||||
this.prevchannel = this.localuser.channelids.get(this.perminfo.prevchannel);
|
||||
}
|
||||
get perminfo() {
|
||||
return this.localuser.perminfo.guilds[this.id];
|
||||
}
|
||||
set perminfo(e) {
|
||||
this.localuser.perminfo.guilds[this.id] = e;
|
||||
}
|
||||
notisetting(settings: {
|
||||
channel_overrides?: unknown[];
|
||||
message_notifications: any;
|
||||
flags?: number;
|
||||
hide_muted_channels?: boolean;
|
||||
mobile_push?: boolean;
|
||||
mute_config?: null;
|
||||
mute_scheduled_events?: boolean;
|
||||
muted?: boolean;
|
||||
notify_highlights?: number;
|
||||
suppress_everyone?: boolean;
|
||||
suppress_roles?: boolean;
|
||||
version?: number;
|
||||
guild_id?: string;
|
||||
}) {
|
||||
this.message_notifications = settings.message_notifications;
|
||||
}
|
||||
setnotifcation() {
|
||||
let noti = this.message_notifications;
|
||||
const notiselect = new Dialog([
|
||||
"vdiv",
|
||||
[
|
||||
"radio",
|
||||
"select notifications type",
|
||||
["all", "only mentions", "none"],
|
||||
function (e: string /* "all" | "only mentions" | "none" */) {
|
||||
noti = ["all", "only mentions", "none"].indexOf(e);
|
||||
},
|
||||
noti,
|
||||
],
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"submit",
|
||||
(_: any) => {
|
||||
//
|
||||
fetch(this.info.api + `/users/@me/guilds/${this.id}/settings/`, {
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify({
|
||||
message_notifications: noti,
|
||||
}),
|
||||
});
|
||||
this.message_notifications = noti;
|
||||
},
|
||||
],
|
||||
]);
|
||||
notiselect.show();
|
||||
}
|
||||
confirmleave() {
|
||||
const full = new Dialog([
|
||||
"vdiv",
|
||||
["title", "Are you sure you want to leave?"],
|
||||
[
|
||||
"hdiv",
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"Yes, I'm sure",
|
||||
(_: any) => {
|
||||
this.leave().then((_) => {
|
||||
full.hide();
|
||||
});
|
||||
},
|
||||
],
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"Nevermind",
|
||||
(_: any) => {
|
||||
full.hide();
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
full.show();
|
||||
}
|
||||
async leave() {
|
||||
return fetch(this.info.api + "/users/@me/guilds/" + this.id, {
|
||||
method: "DELETE",
|
||||
headers: this.headers,
|
||||
});
|
||||
}
|
||||
printServers() {
|
||||
let build = "";
|
||||
for (const thing of this.headchannels) {
|
||||
build += thing.name + ":" + thing.position + "\n";
|
||||
for (const thingy of thing.children) {
|
||||
build += " " + thingy.name + ":" + thingy.position + "\n";
|
||||
}
|
||||
}
|
||||
console.log(build);
|
||||
}
|
||||
calculateReorder() {
|
||||
let position = -1;
|
||||
const build: {
|
||||
id: string;
|
||||
position: number | undefined;
|
||||
parent_id: string | undefined;
|
||||
}[] = [];
|
||||
for (const thing of this.headchannels) {
|
||||
const thisthing: {
|
||||
id: string;
|
||||
position: number | undefined;
|
||||
parent_id: string | undefined;
|
||||
} = { id: thing.id, position: undefined, parent_id: undefined };
|
||||
if (thing.position <= position) {
|
||||
thing.position = thisthing.position = position + 1;
|
||||
}
|
||||
position = thing.position;
|
||||
console.log(position);
|
||||
if (thing.move_id && thing.move_id !== thing.parent_id) {
|
||||
thing.parent_id = thing.move_id;
|
||||
thisthing.parent_id = thing.parent?.id;
|
||||
thing.move_id = undefined;
|
||||
}
|
||||
if (thisthing.position || thisthing.parent_id) {
|
||||
build.push(thisthing);
|
||||
}
|
||||
if (thing.children.length > 0) {
|
||||
const things = thing.calculateReorder();
|
||||
for (const thing of things) {
|
||||
build.push(thing);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(build);
|
||||
this.printServers();
|
||||
if (build.length === 0) {
|
||||
return;
|
||||
}
|
||||
const serverbug = false;
|
||||
if (serverbug) {
|
||||
for (const thing of build) {
|
||||
console.log(build, thing);
|
||||
fetch(this.info.api + "/guilds/" + this.id + "/channels", {
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify([thing]),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fetch(this.info.api + "/guilds/" + this.id + "/channels", {
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(build),
|
||||
});
|
||||
}
|
||||
}
|
||||
get localuser() {
|
||||
return this.owner;
|
||||
}
|
||||
get info() {
|
||||
return this.owner.info;
|
||||
}
|
||||
sortchannels() {
|
||||
this.headchannels.sort((a, b) => {
|
||||
return a.position - b.position;
|
||||
});
|
||||
}
|
||||
static generateGuildIcon(
|
||||
guild: Guild | (invitejson["guild"] & { info: { cdn: string } })
|
||||
) {
|
||||
const divy = document.createElement("div");
|
||||
divy.classList.add("servernoti");
|
||||
|
||||
const noti = document.createElement("div");
|
||||
noti.classList.add("unread");
|
||||
divy.append(noti);
|
||||
if (guild instanceof Guild) {
|
||||
guild.localuser.guildhtml.set(guild.id, divy);
|
||||
}
|
||||
let icon: string | null;
|
||||
if (guild instanceof Guild) {
|
||||
icon = guild.properties.icon;
|
||||
} else {
|
||||
icon = guild.icon;
|
||||
}
|
||||
if (icon !== null) {
|
||||
const img = document.createElement("img");
|
||||
img.classList.add("pfp", "servericon");
|
||||
img.src = guild.info.cdn + "/icons/" + guild.id + "/" + icon + ".png";
|
||||
divy.appendChild(img);
|
||||
if (guild instanceof Guild) {
|
||||
img.onclick = () => {
|
||||
console.log(guild.loadGuild);
|
||||
guild.loadGuild();
|
||||
guild.loadChannel();
|
||||
};
|
||||
Guild.contextmenu.bindContextmenu(img, guild, undefined);
|
||||
}
|
||||
} else {
|
||||
const div = document.createElement("div");
|
||||
let name: string;
|
||||
if (guild instanceof Guild) {
|
||||
name = guild.properties.name;
|
||||
} else {
|
||||
name = guild.name;
|
||||
}
|
||||
const build = name
|
||||
.replace(/'s /g, " ")
|
||||
.replace(/\w+/g, (word) => word[0])
|
||||
.replace(/\s/g, "");
|
||||
div.textContent = build;
|
||||
div.classList.add("blankserver", "servericon");
|
||||
divy.appendChild(div);
|
||||
if (guild instanceof Guild) {
|
||||
div.onclick = () => {
|
||||
guild.loadGuild();
|
||||
guild.loadChannel();
|
||||
};
|
||||
Guild.contextmenu.bindContextmenu(div, guild, undefined);
|
||||
}
|
||||
}
|
||||
return divy;
|
||||
}
|
||||
generateGuildIcon() {
|
||||
return Guild.generateGuildIcon(this);
|
||||
}
|
||||
confirmDelete() {
|
||||
let confirmname = "";
|
||||
const full = new Dialog([
|
||||
"vdiv",
|
||||
[
|
||||
"title",
|
||||
"Are you sure you want to delete " + this.properties.name + "?",
|
||||
],
|
||||
[
|
||||
"textbox",
|
||||
"Name of server:",
|
||||
"",
|
||||
function (this: HTMLInputElement) {
|
||||
confirmname = this.value;
|
||||
},
|
||||
],
|
||||
[
|
||||
"hdiv",
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"Yes, I'm sure",
|
||||
(_: any) => {
|
||||
console.log(confirmname);
|
||||
if (confirmname !== this.properties.name) {
|
||||
return;
|
||||
}
|
||||
this.delete().then((_) => {
|
||||
full.hide();
|
||||
});
|
||||
},
|
||||
],
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"Nevermind",
|
||||
(_: any) => {
|
||||
full.hide();
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
full.show();
|
||||
}
|
||||
async delete() {
|
||||
return fetch(this.info.api + "/guilds/" + this.id + "/delete", {
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
});
|
||||
}
|
||||
unreads(html?: HTMLElement | undefined) {
|
||||
if (html) {
|
||||
this.html = html;
|
||||
} else {
|
||||
html = this.html;
|
||||
}
|
||||
let read = true;
|
||||
for (const thing of this.channels) {
|
||||
if (thing.hasunreads) {
|
||||
console.log(thing);
|
||||
read = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!html) {
|
||||
return;
|
||||
}
|
||||
if (read) {
|
||||
html.children[0].classList.remove("notiunread");
|
||||
} else {
|
||||
html.children[0].classList.add("notiunread");
|
||||
}
|
||||
}
|
||||
getHTML() {
|
||||
//this.printServers();
|
||||
this.sortchannels();
|
||||
this.printServers();
|
||||
const build = document.createElement("div");
|
||||
|
||||
for (const thing of this.headchannels) {
|
||||
build.appendChild(thing.createguildHTML(this.isAdmin()));
|
||||
}
|
||||
return build;
|
||||
}
|
||||
isAdmin() {
|
||||
return this.member.isAdmin();
|
||||
}
|
||||
async markAsRead() {
|
||||
const build: {
|
||||
read_states: {
|
||||
channel_id: string;
|
||||
message_id: string | null | undefined;
|
||||
read_state_type: number;
|
||||
}[];
|
||||
} = { read_states: [] };
|
||||
for (const thing of this.channels) {
|
||||
if (thing.hasunreads) {
|
||||
build.read_states.push({
|
||||
channel_id: thing.id,
|
||||
message_id: thing.lastmessageid,
|
||||
read_state_type: 0,
|
||||
});
|
||||
thing.lastreadmessageid = thing.lastmessageid;
|
||||
if (!thing.myhtml) continue;
|
||||
thing.myhtml.classList.remove("cunread");
|
||||
}
|
||||
}
|
||||
this.unreads();
|
||||
fetch(this.info.api + "/read-states/ack-bulk", {
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify(build),
|
||||
});
|
||||
}
|
||||
hasRole(r: Role | string) {
|
||||
console.log("this should run");
|
||||
if (r instanceof Role) {
|
||||
r = r.id;
|
||||
}
|
||||
return this.member.hasRole(r);
|
||||
}
|
||||
loadChannel(ID?: string | undefined) {
|
||||
if (ID) {
|
||||
const channel = this.localuser.channelids.get(ID);
|
||||
if (channel) {
|
||||
channel.getHTML();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.prevchannel) {
|
||||
console.log(this.prevchannel);
|
||||
this.prevchannel.getHTML();
|
||||
return;
|
||||
}
|
||||
for (const thing of this.channels) {
|
||||
if (thing.children.length === 0) {
|
||||
thing.getHTML();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
loadGuild() {
|
||||
this.localuser.loadGuild(this.id);
|
||||
}
|
||||
updateChannel(json: channeljson) {
|
||||
const channel = this.localuser.channelids.get(json.id);
|
||||
if (channel) {
|
||||
channel.updateChannel(json);
|
||||
this.headchannels = [];
|
||||
for (const thing of this.channels) {
|
||||
thing.children = [];
|
||||
}
|
||||
this.headchannels = [];
|
||||
for (const thing of this.channels) {
|
||||
const parent = thing.resolveparent(this);
|
||||
if (!parent) {
|
||||
this.headchannels.push(thing);
|
||||
}
|
||||
}
|
||||
this.printServers();
|
||||
}
|
||||
}
|
||||
createChannelpac(json: channeljson) {
|
||||
const thischannel = new Channel(json, this);
|
||||
this.localuser.channelids.set(json.id, thischannel);
|
||||
this.channels.push(thischannel);
|
||||
thischannel.resolveparent(this);
|
||||
if (!thischannel.parent) {
|
||||
this.headchannels.push(thischannel);
|
||||
}
|
||||
this.calculateReorder();
|
||||
this.printServers();
|
||||
return thischannel;
|
||||
}
|
||||
createchannels(func = this.createChannel) {
|
||||
let name = "";
|
||||
let category = 0;
|
||||
const channelselect = new Dialog([
|
||||
"vdiv",
|
||||
[
|
||||
"radio",
|
||||
"select channel type",
|
||||
["voice", "text", "announcement"],
|
||||
function (radio: string) {
|
||||
console.log(radio);
|
||||
category =
|
||||
{ text: 0, voice: 2, announcement: 5, category: 4 }[radio] || 0;
|
||||
},
|
||||
1,
|
||||
],
|
||||
[
|
||||
"textbox",
|
||||
"Name of channel",
|
||||
"",
|
||||
function (this: HTMLInputElement) {
|
||||
name = this.value;
|
||||
},
|
||||
],
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"submit",
|
||||
function () {
|
||||
console.log(name, category);
|
||||
func(name, category);
|
||||
channelselect.hide();
|
||||
},
|
||||
],
|
||||
]);
|
||||
channelselect.show();
|
||||
}
|
||||
createcategory() {
|
||||
let name = "";
|
||||
const category = 4;
|
||||
const channelselect = new Dialog([
|
||||
"vdiv",
|
||||
[
|
||||
"textbox",
|
||||
"Name of category",
|
||||
"",
|
||||
function (this: HTMLInputElement) {
|
||||
name = this.value;
|
||||
},
|
||||
],
|
||||
[
|
||||
"button",
|
||||
"",
|
||||
"submit",
|
||||
() => {
|
||||
console.log(name, category);
|
||||
this.createChannel(name, category);
|
||||
channelselect.hide();
|
||||
},
|
||||
],
|
||||
]);
|
||||
channelselect.show();
|
||||
}
|
||||
delChannel(json: channeljson) {
|
||||
const channel = this.localuser.channelids.get(json.id);
|
||||
this.localuser.channelids.delete(json.id);
|
||||
if (!channel) return;
|
||||
this.channels.splice(this.channels.indexOf(channel), 1);
|
||||
const indexy = this.headchannels.indexOf(channel);
|
||||
if (indexy !== -1) {
|
||||
this.headchannels.splice(indexy, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
const build=[];
|
||||
for(const thing of this.channels){
|
||||
console.log(thing.id);
|
||||
if(thing!==channel){
|
||||
build.push(thing)
|
||||
}else{
|
||||
console.log("fail");
|
||||
if(thing.parent){
|
||||
thing.parent.delChannel(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.channels=build;
|
||||
*/
|
||||
this.printServers();
|
||||
}
|
||||
createChannel(name: string, type: number) {
|
||||
fetch(this.info.api + "/guilds/" + this.id + "/channels", {
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify({ name, type }),
|
||||
});
|
||||
}
|
||||
async createRole(name: string) {
|
||||
const fetched = await fetch(
|
||||
this.info.api + "/guilds/" + this.id + "roles",
|
||||
{
|
||||
method: "POST",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify({
|
||||
name,
|
||||
color: 0,
|
||||
permissions: "0",
|
||||
}),
|
||||
}
|
||||
);
|
||||
const json = await fetched.json();
|
||||
const role = new Role(json, this);
|
||||
this.roleids.set(role.id, role);
|
||||
this.roles.push(role);
|
||||
return role;
|
||||
}
|
||||
async updateRolePermissions(id: string, perms: Permissions) {
|
||||
const role = this.roleids.get(id);
|
||||
if (!role) {
|
||||
return;
|
||||
}
|
||||
role.permissions.allow = perms.allow;
|
||||
role.permissions.deny = perms.deny;
|
||||
|
||||
await fetch(this.info.api + "/guilds/" + this.id + "/roles/" + role.id, {
|
||||
method: "PATCH",
|
||||
headers: this.headers,
|
||||
body: JSON.stringify({
|
||||
color: role.color,
|
||||
hoist: role.hoist,
|
||||
icon: role.icon,
|
||||
mentionable: role.mentionable,
|
||||
name: role.name,
|
||||
permissions: role.permissions.allow.toString(),
|
||||
unicode_emoji: role.unicode_emoji,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
Guild.setupcontextmenu();
|
||||
export { Guild };
|
Loading…
Add table
Add a link
Reference in a new issue