From 6196c4ac8502d6f094fec21641df4abb4a26abd0 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Wed, 25 Sep 2024 11:29:42 -0500 Subject: [PATCH] more bot settings --- src/webpage/bot.ts | 251 +++++++++++++++++++++++++++++++++++++++ src/webpage/jsontypes.ts | 108 ++++++++--------- src/webpage/localuser.ts | 40 ++++++- src/webpage/settings.ts | 44 +++++-- src/webpage/style.css | 8 +- 5 files changed, 382 insertions(+), 69 deletions(-) create mode 100644 src/webpage/bot.ts diff --git a/src/webpage/bot.ts b/src/webpage/bot.ts new file mode 100644 index 0000000..eb0fd89 --- /dev/null +++ b/src/webpage/bot.ts @@ -0,0 +1,251 @@ +import { mainuserjson } from "./jsontypes.js"; +import { Localuser } from "./localuser.js"; +import { MarkDown } from "./markdown.js"; +import { Settings } from "./settings.js"; +import { User } from "./user.js"; +import {guildjson} from "./jsontypes.js"; +class Bot{ + readonly owner:Localuser; + readonly token:string; + readonly json:mainuserjson; + headers: { "Content-type": string; Authorization: string }; + get localuser(){ + return this.owner; + } + get info(){ + return this.localuser.info; + } + constructor(json:mainuserjson,token:string,owner:Localuser){ + this.owner=owner; + this.token=token; + this.json=json; + this.headers={ + "Content-type": "application/json; charset=UTF-8", + Authorization: token, + }; + } + settings(){ + const settings = new Settings("Bot Settings"); + const botOptions = settings.addButton("Profile",{ltr:true}); + const bot=new User(this.json,this.localuser); + { + const hypotheticalProfile = document.createElement("div"); + + let file: undefined | File | null; + let newpronouns: string | undefined; + let newbio: string | undefined; + const hypouser = bot.clone(); + let color: string; + async function regen(){ + hypotheticalProfile.textContent = ""; + const hypoprofile = await hypouser.buildprofile(-1, -1); + + hypotheticalProfile.appendChild(hypoprofile); + } + regen(); + const settingsLeft = botOptions.addOptions(""); + const settingsRight = botOptions.addOptions(""); + settingsRight.addHTMLArea(hypotheticalProfile); + + const finput = settingsLeft.addFileInput( + "Upload pfp:", + _=>{ + if(file){ + this.updatepfp(file); + } + }, + { clear: true } + ); + finput.watchForChange(_=>{ + if(!_){ + file = null; + hypouser.avatar = null; + hypouser.hypotheticalpfp = true; + regen(); + return; + } + if(_.length){ + file = _[0]; + const blob = URL.createObjectURL(file); + hypouser.avatar = blob; + hypouser.hypotheticalpfp = true; + regen(); + } + }); + let bfile: undefined | File | null; + const binput = settingsLeft.addFileInput( + "Upload banner:", + _=>{ + if(bfile !== undefined){ + this.updatebanner(bfile); + } + }, + { clear: true } + ); + binput.watchForChange(_=>{ + if(!_){ + bfile = null; + hypouser.banner = undefined; + hypouser.hypotheticalbanner = true; + regen(); + return; + } + if(_.length){ + bfile = _[0]; + const blob = URL.createObjectURL(bfile); + hypouser.banner = blob; + hypouser.hypotheticalbanner = true; + regen(); + } + }); + let changed = false; + const pronounbox = settingsLeft.addTextInput( + "Pronouns", + _=>{ + if(newpronouns || newbio || changed){ + this.updateProfile({ + pronouns: newpronouns, + bio: newbio, + accent_color: Number.parseInt("0x" + color.substr(1), 16), + }); + } + }, + { initText: bot.pronouns } + ); + pronounbox.watchForChange(_=>{ + hypouser.pronouns = _; + newpronouns = _; + regen(); + }); + const bioBox = settingsLeft.addMDInput("Bio:", _=>{}, { + initText: bot.bio.rawString, + }); + bioBox.watchForChange(_=>{ + newbio = _; + hypouser.bio = new MarkDown(_, this.owner); + regen(); + }); + + if(bot.accent_color){ + color = "#" + bot.accent_color.toString(16); + }else{ + color = "transparent"; + } + const colorPicker = settingsLeft.addColorInput( + "Profile color", + _=>{}, + { initColor: color } + ); + colorPicker.watchForChange(_=>{ + console.log(); + color = _; + hypouser.accent_color = Number.parseInt("0x" + _.substr(1), 16); + changed = true; + regen(); + }); + } + { + const guildsettings=settings.addButton("Guilds"); + guildsettings.addTitle("Guilds bot is in:"); + fetch(this.info.api+"/users/@me/guilds/",{ + headers:this.headers + }).then(_=>_.json()).then((json:(guildjson["properties"])[])=>{ + for(const guild of json){ + const content = document.createElement("div"); + content.classList.add("discovery-guild"); + + if(guild.banner){ + const banner = document.createElement("img"); + banner.classList.add("banner"); + banner.crossOrigin = "anonymous"; + banner.src = this.info.cdn + "/icons/" + guild.id + "/" + guild.banner + ".png?size=256"; + banner.alt = ""; + content.appendChild(banner); + } + + const nameContainer = document.createElement("div"); + nameContainer.classList.add("flex"); + const img = document.createElement("img"); + img.classList.add("icon"); + img.crossOrigin = "anonymous"; + img.src = this.info.cdn + (guild.icon? "/icons/" + guild.id + "/" + guild.icon + ".png?size=48": "/embed/avatars/3.png"); + img.alt = ""; + nameContainer.appendChild(img); + + const name = document.createElement("h3"); + name.textContent = guild.name; + nameContainer.appendChild(name); + content.appendChild(nameContainer); + const desc = document.createElement("p"); + desc.textContent = guild.description; + content.appendChild(desc); + + + guildsettings.addHTMLArea(content); + content.onclick=()=>{ + const guildsetting=guildsettings.addSubOptions(guild.name); + guildsetting.addHTMLArea(content); + guildsetting.addButtonInput("","Leave Guild",()=>{ + if(confirm(`Are you sure you want to leave ${guild.name}?`)){ + fetch(this.info.api+"/users/@me/guilds/"+guild.id,{ + method:"DELETE", + headers:this.headers + }) + } + }) + } + } + }) + } + settings.show(); + } + + updatepfp(file: Blob): void{ + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = ()=>{ + fetch(this.info.api + "/users/@me", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({ + avatar: reader.result, + }), + }); + }; + } + updatebanner(file: Blob | null): void{ + if(file){ + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = ()=>{ + fetch(this.info.api + "/users/@me", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({ + banner: reader.result, + }), + }); + }; + }else{ + fetch(this.info.api + "/users/@me", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({ + banner: null, + }), + }); + } + } + updateProfile(json: { + bio?: string; + pronouns?: string; + accent_color?: number; + }){ + fetch(this.info.api + "/users/@me/profile", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify(json), + }); + } +} +export {Bot}; diff --git a/src/webpage/jsontypes.ts b/src/webpage/jsontypes.ts index 6be9e9c..26a1bcc 100644 --- a/src/webpage/jsontypes.ts +++ b/src/webpage/jsontypes.ts @@ -168,60 +168,60 @@ animated?: boolean; }; type guildjson = { -application_command_counts: { [key: string]: number }; -channels: channeljson[]; -data_mode: string; -emojis: emojijson[]; -guild_scheduled_events: []; -id: string; -large: boolean; -lazy: boolean; -member_count: number; -premium_subscription_count: number; -properties: { -region: string | null; -name: string; -description: string; -icon: string; -splash: string; -banner: string; -features: string[]; -preferred_locale: string; -owner_id: string; -application_id: string; -afk_channel_id: string; -afk_timeout: number; -member_count: number; -system_channel_id: string; -verification_level: number; -explicit_content_filter: number; -default_message_notifications: number; -mfa_level: number; -vanity_url_code: number; -premium_tier: number; -premium_progress_bar_enabled: boolean; -system_channel_flags: number; -discovery_splash: string; -rules_channel_id: string; -public_updates_channel_id: string; -max_video_channel_users: number; -max_members: number; -nsfw_level: number; -hub_type: null; -home_header: null; -id: string; -latest_onboarding_question_id: string; -max_stage_video_channel_users: number; -nsfw: boolean; -safety_alerts_channel_id: string; -}; -roles: rolesjson[]; -stage_instances: []; -stickers: []; -threads: []; -version: string; -guild_hashes: {}; -joined_at: string; + application_command_counts: { [key: string]: number }; + channels: channeljson[]; + data_mode: string; + emojis: emojijson[]; + guild_scheduled_events: []; + id: string; + large: boolean; + lazy: boolean; + member_count: number; + premium_subscription_count: number; + properties: { + region: string | null; + name: string; + description: string; + icon: string; + splash: string; + banner: string; + features: string[]; + preferred_locale: string; + owner_id: string; + application_id: string; + afk_channel_id: string; + afk_timeout: number; + member_count: number; + system_channel_id: string; + verification_level: number; + explicit_content_filter: number; + default_message_notifications: number; + mfa_level: number; + vanity_url_code: number; + premium_tier: number; + premium_progress_bar_enabled: boolean; + system_channel_flags: number; + discovery_splash: string; + rules_channel_id: string; + public_updates_channel_id: string; + max_video_channel_users: number; + max_members: number; + nsfw_level: number; + hub_type: null; + home_header: null; + id: string; + latest_onboarding_question_id: string; + max_stage_video_channel_users: number; + nsfw: boolean; + safety_alerts_channel_id: string; + }; + roles: rolesjson[]; + stage_instances: []; + stickers: []; + threads: []; + version: string; + guild_hashes: {}; + joined_at: string; }; type startTypingjson = { d: { diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index c9d58b8..36f7c7c 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -19,6 +19,7 @@ import{ import{ Member }from"./member.js"; import{ Form, FormError, Options, Settings }from"./settings.js"; import{ MarkDown }from"./markdown.js"; +import { Bot } from "./bot.js"; const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]); @@ -1370,6 +1371,11 @@ class Localuser{ } readonly botTokens:Map=new Map(); async manageApplication(appId = "", container:Options){ + if(this.perminfo.applications){ + for(const item of Object.keys(this.perminfo.applications)){ + this.botTokens.set(item,this.perminfo.applications[item]); + } + } const res = await fetch(this.info.api + "/applications/" + appId, { headers: this.headers, }); @@ -1436,10 +1442,40 @@ class Localuser{ const updateJSON = await updateRes.json(); text.setText("Token: "+updateJSON.token); this.botTokens.set(appId,updateJSON.token); + if(this.perminfo.applications[appId]){ + this.perminfo.applications[appId]=updateJSON.token; + this.userinfo.updateLocal(); + } }); - const text=form.addText(this.botTokens.has(appId)?"Token: "+this.botTokens.get(appId):"Token: *****************") + const text=form.addText(this.botTokens.has(appId)?"Token: "+this.botTokens.get(appId):"Token: *****************"); + const check=form.addOptions("",{noSubmit:true}); + if(!this.perminfo.applications){ + this.perminfo.applications={}; + this.userinfo.updateLocal(); + } + const checkbox=check.addCheckboxInput("Save token to localStorage",()=>{},{initState:!!this.perminfo.applications[appId]}); + checkbox.watchForChange(_=>{ + if(_){ + if(this.botTokens.has(appId)){ + this.perminfo.applications[appId]=this.botTokens.get(appId); + this.userinfo.updateLocal(); + }else{ + alert("Don't know token so can't save it to localStorage, sorry"); + checkbox.setState(false); + } + }else{ + delete this.perminfo.applications[appId]; + this.userinfo.updateLocal(); + } + }); + form.addButtonInput("","Advanced bot settings",()=>{ + const token=this.botTokens.get(appId); + if(token){ + const botc=new Bot(bot,token,this); + botc.settings(); + } + }) } - //---------- resolving members code ----------- readonly waitingmembers: Map< string, diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index cc01f85..85d7c81 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -206,6 +206,15 @@ class CheckboxInput implements OptionsElement{ this.value = value; } } + setState(state:boolean){ + if(this.input){ + const checkbox=this.input.deref(); + if(checkbox){ + checkbox.checked=state; + this.value=state; + } + } + } onchange: (str: boolean) => void = _=>{}; watchForChange(func: (str: boolean) => void){ this.onchange = func; @@ -494,20 +503,21 @@ class Options implements OptionsElement{ readonly owner: Buttons | Options | Form; readonly ltr: boolean; value!: void; - readonly html: WeakMap, WeakRef> = - new WeakMap(); + readonly html: WeakMap, WeakRef> = new WeakMap(); + readonly noSubmit:boolean=false; container: WeakRef = new WeakRef( document.createElement("div") ); constructor( name: string, owner: Buttons | Options | Form, - { ltr = false } = {} + { ltr = false, noSubmit=false} = {} ){ this.name = name; this.options = []; this.owner = owner; this.ltr = ltr; + this.noSubmit=noSubmit; } removeAll(){ while(this.options.length){ @@ -519,8 +529,8 @@ class Options implements OptionsElement{ } } watchForChange(){} - addOptions(name: string, { ltr = false } = {}){ - const options = new Options(name, this, { ltr }); + addOptions(name: string, { ltr = false,noSubmit=false } = {}){ + const options = new Options(name, this, { ltr,noSubmit }); this.options.push(options); this.generate(options); return options; @@ -542,8 +552,8 @@ class Options implements OptionsElement{ ); } } - addSubOptions(name: string, { ltr = false } = {}){ - const options = new Options(name, this, { ltr }); + addSubOptions(name: string, { ltr = false,noSubmit=false } = {}){ + const options = new Options(name, this, { ltr,noSubmit }); this.subOptions = options; this.genTop(); return options; @@ -788,7 +798,10 @@ class Options implements OptionsElement{ } } changed(){ - if(this.owner instanceof Options || this.owner instanceof Form){ + if(this.noSubmit){ + return; + } + if(this.owner instanceof Options || this.owner instanceof Form ){ this.owner.changed(); return; } @@ -877,11 +890,11 @@ class Form implements OptionsElement{ //the value can't really be anything, but I don't care enough to fix this this.values[key] = value; } - addSubOptions(name: string, { ltr = false } = {}){ + addSubOptions(name: string, { ltr = false,noSubmit=false } = {}){ if(this.button&&this.button.deref()){ (this.button.deref() as HTMLElement).hidden=true; } - return this.options.addSubOptions(name,{ltr}); + return this.options.addSubOptions(name,{ltr, noSubmit}); } addSubForm( name: string, @@ -981,9 +994,20 @@ class Form implements OptionsElement{ } return mdInput; } + /** + * This function does not integrate with the form, so be aware of that + * + */ addButtonInput(label:string,textContent:string,onSubmit:()=>void){ return this.options.addButtonInput(label,textContent,onSubmit); } + /** + * This function does not integrate with the form, so be aware of that + * + */ + addOptions(name: string, { ltr = false,noSubmit=false } = {}){ + return this.options.addOptions(name, {ltr,noSubmit}); + } addCheckboxInput( label: string, formName: string, diff --git a/src/webpage/style.css b/src/webpage/style.css index d337554..2321841 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -1427,7 +1427,10 @@ span { } .discovery-guild img.banner { - width: 215px; + width: 100%; + height: .6in; + border-radius: .04in; + margin-bottom: .03in; } .discovery-guild img.icon { @@ -1875,7 +1878,6 @@ form div{ background:var(--primary-bg); } .banner{ - position:absolute; top:0; left:0; width:100%; @@ -2205,4 +2207,4 @@ form div{ } .mentionMD:hover{ background:color-mix(in srgb,var(--mention-md-bg),white 10%); -} \ No newline at end of file +}