Integrations (#6)

Integrations are now complete
This commit is contained in:
CatAClock 2025-05-03 02:44:21 +00:00
parent 3b48e97a5e
commit c1dc53cace
16 changed files with 371 additions and 149 deletions

View file

@ -205,6 +205,13 @@ html {
border-width: 1px; border-width: 1px;
} }
.Posting {
border-style: solid;
border-width: 1px;
margin-right: 5%;
}
.MainFooter { .MainFooter {
display: flex; display: flex;
margin-top: 75vh; margin-top: 75vh;

1
CSS/post.css Normal file
View file

@ -0,0 +1 @@

0
CSS/setting.css Normal file
View file

View file

@ -16,7 +16,7 @@
<section style="position: absolute; width: 100%; height: 100%" class="Notifications"></section> <section style="position: absolute; width: 100%; height: 100%" class="Notifications"></section>
<header style="position: relative; z-index: 1;"> <header style="position: relative; z-index: 1;">
<h1>Mail</h1> <h1>Mail</h1>
<p onclick="history.back()"><b>OK</b></p> <p onclick="window.location.href = window.location.origin"><b>Back</b></p>
</header> </header>
</body> </body>
</html> </html>

21
HTML/post.html Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>The Fediverse</title>
<meta name="description" content="Change the fucking channel already!">
<link rel="icon" href="../Icons/favicon.ico" />
<link rel="stylesheet" href="../CSS/post.css">
<script type="module" src="../JS/post.js"></script>
<!-- Dependenci -->
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
</head>
<body style="margin: 0px; text-align: center;">
<header>
<h1>Post</h1>
<textarea cols="50" rows="25" class="text" placeholder="status here..."></textarea>
<p class="button">POST!</p>
</header>
<p onclick="window.location.href = window.location.origin"><b>Back</b></p>
</body>
</html>

View file

@ -4,7 +4,7 @@
<title>The Fediverse</title> <title>The Fediverse</title>
<meta name="description" content="Change the fucking channel already!"> <meta name="description" content="Change the fucking channel already!">
<link rel="icon" href="../Icons/favicon.ico" /> <link rel="icon" href="../Icons/favicon.ico" />
<link rel="stylesheet" href="../CSS/setting.css">
<script type="module" src="../JS/setting.js"></script> <script type="module" src="../JS/setting.js"></script>
<!-- Dependenci --> <!-- Dependenci -->
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script> <script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
@ -17,11 +17,11 @@
<p class="Local">Toggle Local</p> <p class="Local">Toggle Local</p>
<p class="Remote">Toggle Remote</p> <p class="Remote">Toggle Remote</p>
<p class="Login Mastodon"><em>Login to Mastodon</em></p> <p class="Login Mastodon"><em>Login to Mastodon</em></p>
<input type="text" minlength="6" class="WebInput Mastodon" required /> <input type="text" class="WebInput Mastodon" placeholder="The website your account is on."/>
<p class="Logout Mastodon" style="visibility: hidden;"><em>Logout of Mastodon</em></p> <p class="Logout Mastodon" style="visibility: hidden;"><em>Logout of Mastodon</em></p>
<p class="Login Bluesky"><em>Login to Bluesky</em></p> <p class="Login Bluesky"><em>Login to Bluesky</em></p>
<input type="text" minlength="6" class="WebInput Bluesky" required /> <input type="text" class="WebInput Bluesky" />
<p class="Logout Bluesky" style="visibility: hidden;"><em>Logout of Bluesky</em></p> <p class="Logout Bluesky" style="visibility: hidden;"><em>Logout of Bluesky</em></p>
<p onclick="history.back()"><b>OK</b></p> <p onclick="window.location.href = window.location.origin"><b>Back</b></p>
</body> </body>
</html> </html>

View file

@ -1,14 +1,54 @@
import * as Cookie from "./Cookies.js";
export async function GetBlueskyDID(PDS, Handle) { export async function GetBlueskyDID(PDS, Handle) {
let request = fetch(PDS + "/xrpc/com.atproto.identity.resolveDid?handle=" + Handle, { method: "GET"}) let DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/com.atproto.identity.resolveHandle?handle=" + Handle);
.then((response) => response.json()); let request = fetch(PDS + "/xrpc/com.atproto.identity.resolveHandle?handle=" + Handle, { method: "GET", headers: {"Authorization": "DPoP " + Cookie.BlueskyAccessTokenCookie, "DPoP": DPoP}});
return request; let body = await request.then((response) => response.json());
let status = await request.then((response) => response.status);
let header = await request.then((response) => response.headers.get("dpop-nonce"));
if (status == 401) {
await FixNonceMismatch(header);
request = fetch(PDS + "/xrpc/com.atproto.identity.resolveHandle?handle=" + Handle, { method: "GET", headers: {"Authorization": "DPoP " + Cookie.BlueskyAccessTokenCookie, "DPoP": DPoP}});
body = await request.then((response) => response.json());
}
return body;
}
export async function CreatePost(PDS, DID, Text) {
let Json = {
"$type": "app.bsky.feed.post",
"text": Text,
"createdAt": new Date(Date.now()).toISOString()
}
let RequestBody = {
"repo": DID,
"collection": "app.bsky.feed.post",
"record": Json
}
console.log(DID);
console.log(RequestBody.repo);
let DPoP = await ClientDPoPPDS("POST", PDS + "/xrpc/com.atproto.repo.createRecord");
let request = fetch(PDS + "/xrpc/com.atproto.repo.createRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + Cookie.BlueskyAccessTokenCookie, "DPoP": DPoP}});
let body = await request.then((response) => response.json());
let status = await request.then((response) => response.status);
let header = await request.then((response) => response.headers.get("dpop-nonce"));
if (status == 401) {
await FixNonceMismatch(header);
let request = fetch(PDS + "/xrpc/com.atproto.repo.createRecord", { body: JSON.stringify(RequestBody), method: "POST", headers: {"Content-Type": "application/json", "Authorization": "DPoP " + Cookie.BlueskyAccessTokenCookie, "DPoP": DPoP}});
body = await request.then((response) => response.json());
}
return body;
}
// Added after all the components: in case of nonce mismatch...
export async function FixNonceMismatch(head) {
return Cookie.InputCookie(Cookie.BlueskyNonceName, head);
} }
// Component 1/4 // Component 1/4
export async function GetPDSWellKnown() { export async function GetPDSWellKnown() {
let Data = await fetch("https://bsky.social/.well-known/oauth-authorization-server", {method: "GET"}) return await fetch("https://bsky.social/.well-known/oauth-authorization-server", {method: "GET"})
.then((response) => response.json()); .then((response) => response.json());
return Data;
} }
// Component 2/4 // Component 2/4
@ -35,22 +75,23 @@ export async function CreatePKCECodeChallenge(CodeVerifier) {
} }
// Component 3/4 // Component 3/4
export async function PARrequest(PAREndpoint, State, ChallengeCode) { export async function PARrequest(PAREndpoint, State, Challenge) {
return fetch(PAREndpoint, {method: "POST", body: new URLSearchParams({ response_type: "code", code_challenge_method: "S256", scope: "atproto transition:generic", client_id: "https://fedi.crowdedgames.group/oauth/client-metadata.json", redirect_uri: "https://fedi.crowdedgames.group/HTML/setting.html", code_challenge: ChallengeCode, state: State, login_hint: "crowdedgames.group" }), headers: {"Content-Type": "application/x-www-form-urlencoded"}}); return fetch(PAREndpoint, {method: "POST", body: new URLSearchParams({ response_type: "code", code_challenge_method: "S256", scope: "atproto transition:generic", client_id: "https://fedi.crowdedgames.group/oauth/client-metadata.json", redirect_uri: "https://fedi.crowdedgames.group/HTML/setting.html", code_challenge: Challenge, state: State, login_hint: "crowdedgames.group" }), headers: {"Content-Type": "application/x-www-form-urlencoded"}});
} }
export async function AuthRequest(TokenEndpoint, ChallengeVerifier, code, DPoP) { export async function AuthRequest(TokenEndpoint, code, DPoP, Verify) {
return fetch(TokenEndpoint, {method: "POST", body: new URLSearchParams({ grant_type: "authorization_code", code: code, client_id: "https://fedi.crowdedgames.group/oauth/client-metadata.json", redirect_uri: "https://fedi.crowdedgames.group/HTML/setting.html", code_verifier: ChallengeVerifier}), headers: { "DPoP": DPoP, "Content-Type": "application/x-www-form-urlencoded"}}) return fetch(TokenEndpoint, {method: "POST", body: new URLSearchParams({ grant_type: "authorization_code", code: code, client_id: "https://fedi.crowdedgames.group/oauth/client-metadata.json", redirect_uri: "https://fedi.crowdedgames.group/HTML/setting.html", code_verifier: Verify}), headers: { "DPoP": DPoP, "Content-Type": "application/x-www-form-urlencoded"}})
.then((response) => response.json()); .then((response) => response.json());
} }
// Component 4/4 // Component 4/4
export async function ClientDPoP(POSTorGET, RequestURL, DPoPNonce) { export async function ClientDPoPToken(POSTorGET, RequestURL) {
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); let PublicKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPublicKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"]);
let PrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPrivateKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["sign"]);
// Header // Header
var Header = {typ: "dpop+jwt", alg: "ES256", jwk: var Header = {typ: "dpop+jwt", alg: "ES256", jwk:
await crypto.subtle.exportKey("jwk", KeyPair.publicKey) await crypto.subtle.exportKey("jwk", PublicKey)
.then(function(response) { .then(function(response) {
delete response["key_ops"]; delete response["key_ops"];
delete response["ext"]; delete response["ext"];
@ -64,12 +105,12 @@ export async function ClientDPoP(POSTorGET, RequestURL, DPoPNonce) {
Payload.htm = POSTorGET; Payload.htm = POSTorGET;
Payload.htu = RequestURL; Payload.htu = RequestURL;
Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000); Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000);
Payload.nonce = DPoPNonce; Payload.nonce = Cookie.BlueskyNonceCookie;
var sHeader = JSON.stringify(Header); var sHeader = JSON.stringify(Header);
var sPayload = JSON.stringify(Payload); var sPayload = JSON.stringify(Payload);
var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload,
await crypto.subtle.exportKey("jwk", KeyPair.privateKey) await crypto.subtle.exportKey("jwk", PrivateKey)
.then(function(response) { .then(function(response) {
delete response["key_ops"]; delete response["key_ops"];
delete response["ext"]; delete response["ext"];
@ -80,66 +121,85 @@ export async function ClientDPoP(POSTorGET, RequestURL, DPoPNonce) {
} }
// So far does nothing? Don't touch :3 // So far does nothing? Don't touch :3
export async function AssertionJWT() { export async function ClientDPoPPDS(POSTorGET, RequestURL) {
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); let PublicKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPublicKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"]);
let PrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPrivateKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["sign"]);
// Header // Header
var Header = {alg: "HS256", kid: await crypto.subtle.exportKey("jwk", KeyPair.publicKey).then(function(response) {return response})}; var Header = {typ: "dpop+jwt", alg: "ES256", jwk:
await crypto.subtle.exportKey("jwk", PublicKey)
.then(function(response) {
delete response["key_ops"];
delete response["ext"];
delete response["alg"];
return response})
};
// Payload // Payload
var Payload = {}; var Payload = {};
Payload.iss = "https://fedi.crowdedgames.group/oauth/client-metadata.json"; Payload.iss = "https://fedi.crowdedgames.group/oauth/client-metadata.json";
Payload.sub = "https://fedi.crowdedgames.group/oauth/client-metadata.json";
// Payload.aud
Payload.jti = crypto.randomUUID(); Payload.jti = crypto.randomUUID();
Payload.htm = POSTorGET;
Payload.htu = RequestURL;
Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000); Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000);
Payload.nonce = Cookie.BlueskyNonceCookie;
Payload.ath = await CreatePKCECodeChallenge(Cookie.BlueskyAccessTokenCookie);
var sHeader = JSON.stringify(Header); var sHeader = JSON.stringify(Header);
var sPayload = JSON.stringify(Payload); var sPayload = JSON.stringify(Payload);
var JWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "838383"); var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload,
await crypto.subtle.exportKey("jwk", PrivateKey)
.then(function(response) {
delete response["key_ops"];
delete response["ext"];
delete response["alg"];
return response})
);
return JWT;
} }
export async function HandleAuthorization(BlueskyPKCEverifer, BlueskyPKCEchallenge, BlueskyNonce) { export async function HandleAuthorization() {
// Declare Variables // Declare Variables
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
let WellKnown = await GetPDSWellKnown(); let WellKnown = await GetPDSWellKnown();
let PAREndpoint = WellKnown.pushed_authorization_request_endpoint;
let State = crypto.randomUUID(); let State = crypto.randomUUID();
let PKCEverifier = await CreatePKCECodeVerifier(); let PKCEverifier = await CreatePKCECodeVerifier();
let PKCEchallenge = await CreatePKCECodeChallenge(PKCEverifier); let PKCEchallenge = await CreatePKCECodeChallenge(PKCEverifier);
// Save these
Cookie.InputCookie(Cookie.BlueskyPKCEVeriferName, PKCEverifier);
Cookie.InputCookie(Cookie.BlueskyPKCEChallengeName, PKCEchallenge);
// PAR request (beginning) // PAR request (beginning)
let PAR = PARrequest(WellKnown.pushed_authorization_request_endpoint, State, PKCEchallenge); let PAR = PARrequest(WellKnown.pushed_authorization_request_endpoint, State, PKCEchallenge);
let body = await PAR.then((response) => response.json()); let body = await PAR.then((response) => response.json());
let nonce = await PAR.then((response) => response.headers.get("dpop-nonce")); let nonce = await PAR.then((response) => response.headers.get("dpop-nonce"));
// Save nonce
let ExportedKey1 = await crypto.subtle.exportKey("raw", KeyPair.publicKey); Cookie.InputCookie(Cookie.BlueskyNonceName, nonce);
// Save these things. // Export keys
document.cookie = BlueskyPKCEverifer + "=" + PKCEverifier + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; let ExportedKey1 = await crypto.subtle.exportKey("jwk", KeyPair.publicKey);
document.cookie = BlueskyPKCEchallenge + "=" + PKCEchallenge + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; let ExportedKey2 = await crypto.subtle.exportKey("jwk", KeyPair.privateKey);
document.cookie = BlueskyNonce + "=" + nonce + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; // Convert them into a good format.
// Don't remove the keys. They are important. Cookie.InputCookie(Cookie.BlueskyPublicKeyName, JSON.stringify(ExportedKey1));
// if (document.cookie.split("; ").find((row) => row.startsWith(BlueskyPublicKey + "="))?.split("=").length == 1 || document.cookie.split("; ").find((row) => row.startsWith(BlueskyPrivateKey + "="))?.split("=").length == 1) { Cookie.InputCookie(Cookie.BlueskyPrivateKeyName, JSON.stringify(ExportedKey2));
// document.cookie = BlueskyPublicKey + "=" + ExportedKey1 + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;";
// }
// Now we need to authenticate. Make sure the State stays the same throughout this whole process :] // Now we need to authenticate. Make sure the State stays the same throughout this whole process :]
document.location.href = "https://bsky.social/oauth/authorize?client_id=https://fedi.crowdedgames.group/oauth/client-metadata.json&request_uri=" + body.request_uri; document.location.href = "https://bsky.social/oauth/authorize?client_id=https://fedi.crowdedgames.group/oauth/client-metadata.json&request_uri=" + body.request_uri;
} }
export async function GainTokens(PKCEcodeName, NonceName, AccessToken, RefreshToken) { export async function GainTokens() {
let WellKnown = await GetPDSWellKnown();
// Check to see if something's a miss... // Check to see if something's a miss...
if ((document.location.href.split("state=").length > 1 && document.location.href.split("iss=").length > 1 && document.location.href.split("code=").length > 1) && document.cookie.split("; ").find((row) => row.startsWith(PKCEcodeName + "="))?.split("=").length > 1 && document.cookie.split("; ").find((row) => row.startsWith(AccessToken + "="))?.split("=").length == 1) { if ((document.location.href.split("state=").length > 1 && document.location.href.split("iss=").length > 1 && document.location.href.split("code=").length > 1) && Cookie.IsCookieReal(Cookie.BlueskyPKCEVeriferCookie) && !(Cookie.IsCookieReal(Cookie.BlueskyAccessTokenCookie))) {
// Create varaibles, be aware of waits because of internet. // Create varaibles, be aware of waits because of internet.
let DPoP = await ClientDPoP("POST", "https://bsky.social/oauth/token", document.cookie.split("; ").find((row) => row.startsWith(NonceName + "="))?.split("=")[1]); let DPoP = await ClientDPoPToken("POST", WellKnown.token_endpoint);
let PKCE = document.cookie.split("; ").find((row) => row.startsWith(PKCEcodeName + "="))?.split("=")[1];
let code = document.location.href.split("code=")[1]; let code = document.location.href.split("code=")[1];
// Authentication // Authentication
let Auth = await AuthRequest("https://bsky.social/oauth/token", PKCE, code, DPoP); let cookie = await Cookie.BlueskyPKCEVeriferCookie;
let Auth = await AuthRequest(WellKnown.token_endpoint, code, DPoP, cookie);
// Save the tokens and be done // Save the tokens and be done
document.cookie = AccessToken + "=" + Auth.access_token + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; Cookie.InputCookie(Cookie.BlueskyAccessTokenName, Auth.access_token);
document.cookie = RefreshToken + "=" + Auth.refresh_token + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; Cookie.InputCookie(Cookie.BlueskyRefreshTokenName, Auth.refresh_token);
} }
} }
@ -163,3 +223,18 @@ async function sha256(message) {
return string; return string;
} }
// Firefox snippet.
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
// Firefox snippet.
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}

84
JS/Cookies.js Normal file
View file

@ -0,0 +1,84 @@
// STRINGS TODO: make sure to seperate stuff that a user will want to input. Ex: MastodonWebsiteName, BlueskyPDS, etc.
// Mastodon
export const MastodonWebsiteName = "mastodon_website";
export const MastodonClientIDName = "mastodon_client_id";
export const MastodonClientSecretName = "mastodon_client_secret";
export const MastodonAccessTokenName = "mastodon_access_token";
export const MastodonTokenTypeName = "mastodon_token_type";
// Bluesky
export const BlueskyAppName = "https://bsky.app";
export const BlueskyPDSName = "https://bsky.social";
export const BlueskyPKCEVeriferName = "bluesky_pkce_verifier";
export const BlueskyPKCEChallengeName = "bluesky_pkce_challenge";
export const BlueskyPublicKeyName = "bluesky_public_key";
export const BlueskyPrivateKeyName = "bluesky_private_key";
export const BlueskyNonceName = "bluesky_nonce";
export const BlueskyAccessTokenName = "bluesky_access_token";
export const BlueskyRefreshTokenName = "bluesky_refresh_token";
// Tumblr
export const TumblrWebsiteName = "https://www.tumblr.com";
// COOKIES TODO: person inputted stuff (like MastodonWebsiteName) should be implemented.
// Mastodon
export const MastodonWebsiteCookie = GetCookie(MastodonWebsiteName);
export const MastodonClientIDCookie = GetCookie(MastodonClientIDName);
export const MastodonClientSecretCookie = GetCookie(MastodonClientSecretName);
export const MastodonAccessTokenCookie = GetCookie(MastodonAccessTokenName);
export const MastodonTokenTypeCookie = GetCookie(MastodonTokenTypeName);
// Bluesky
export const BlueskyPKCEVeriferCookie = GetCookie(BlueskyPKCEVeriferName);
export const BlueskyPKCEChallengeCookie = GetCookie(BlueskyPKCEChallengeName);
export const BlueskyPublicKeyCookie = GetCookie(BlueskyPublicKeyName);
export const BlueskyPrivateKeyCookie = GetCookie(BlueskyPrivateKeyName);
export const BlueskyNonceCookie = GetCookie(BlueskyNonceName);
export const BlueskyAccessTokenCookie = GetCookie(BlueskyAccessTokenName);
export const BlueskyRefreshTokenCookie = GetCookie(BlueskyRefreshTokenName);
// Tumblr
// None lmao.
// FUNCTIONS
// Get the cookie from cookie storage.
export function GetCookie(CookieName = "") {
// Check if you put in nothing.
if (CookieName == 0) {
console.error("Where is the cookie name? Returning nothing...");
return "";
}
// Get the cookie.
let Cookie = document.cookie.split("; ").find((row) => row.startsWith(CookieName + "="))?.split("=");
// If the cookie doesn't exist...
if (Cookie == undefined || Cookie.length == 1) {
console.log("Cookie not found. Returning nothing...");
return "";
} else {
return Cookie[1];
}
}
// Check if a cookie is real (as in the value isn't nonexistant).
export function IsCookieReal(Cookie = "") {
if (Cookie.length == 0) {
return false;
}
return true;
}
// Remove the cookie.
export function ExpireCookie(CookieName = "") {
document.cookie = CookieName + "=nothing;samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;";
return;
}
// Add a new cookie (or change it's value).
export function InputCookie(CookieName = "", Value = "") {
if (Value == 0 || CookieName == 0) {
console.error("You forgot to put in a value. Stopping...");
return;
}
document.cookie = CookieName + "=" + Value + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"
return;
}

View file

@ -1,8 +1,10 @@
import * as Cookie from "./Cookies.js";
export const Scopes = "read write follow push"; export const Scopes = "read write follow push";
let Origin = document.location.href;
// Gets the public timeline. // Gets the public timeline.
export async function GetPublicTimeline(Local = false, Remote = false, Website) { export async function GetPublicTimeline(Local = false, Remote = false, Website) {
// Cookies can be found in `setting.js`
let Timeline; let Timeline;
if (Local == true && Remote == true) { if (Local == true && Remote == true) {
console.error("Don't set both Local and Remote timelines to true."); console.error("Don't set both Local and Remote timelines to true.");
@ -23,49 +25,56 @@ export async function GetPublicTimeline(Local = false, Remote = false, Website)
} }
// Gets the favorites of a user that you have access to. // Gets the favorites of a user that you have access to.
export async function GetFavorites(Website, MastodonAccessToken, MastodonTokenType) { export async function GetFavorites() {
let Favorites; let Favorites;
// Check for a token. // Check for a token.
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) { if (Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie)) {
let Website = Cookie.MastodonWebsiteCookie;
// Get the varaibles that are stored in cookies. // Get the varaibles that are stored in cookies.
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=")[1]; Favorites = await fetch(Website + "/api/v1/favourites", {method: "GET", headers: {"Authorization": Cookie.MastodonTokenTypeCookie + " " + Cookie.MastodonAccessTokenCookie}})
let TokenType = document.cookie.split("; ").find((row) => row.startsWith(MastodonTokenType + "="))?.split("=")[1];
Favorites = await fetch(Website + "/api/v1/favourites", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
.then((response) => response.json()); .then((response) => response.json());
} }
return Favorites; return Favorites;
} }
// Gets the bookmarks of a user that you have access to. // Gets the bookmarks of a user that you have access to.
export async function GetBookmarks(Website, MastodonAccessToken, MastodonTokenType) { export async function GetBookmarks() {
let Bookmarks; let Bookmarks;
// Check for a token. // Check for a token.
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) { if (Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie)) {
let Website = Cookie.MastodonWebsiteCookie;
// Get the varaibles that are stored in cookies. // Get the varaibles that are stored in cookies.
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=")[1]; Bookmarks = await fetch(Website + "/api/v1/bookmarks", {method: "GET", headers: {"Authorization": Cookie.MastodonTokenTypeCookie + " " + Cookie.MastodonAccessTokenCookie}})
let TokenType = document.cookie.split("; ").find((row) => row.startsWith(MastodonTokenType + "="))?.split("=")[1];
Bookmarks = await fetch(Website + "/api/v1/bookmarks", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
.then((response) => response.json()); .then((response) => response.json());
} }
return Bookmarks; return Bookmarks;
} }
// Gets the notifications of a user that you have access to. // Gets the notifications of a user that you have access to.
export async function GetNotifications(Website, MastodonAccessToken, MastodonTokenType) { export async function GetNotifications() {
let Notifications; let Notifications;
// Check for a token. // Check for a token.
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) { if (Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie)) {
// Get the varaibles that are stored in cookies. let Website = Cookie.MastodonWebsiteCookie;
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=")[1]; // Get the varaibles that are stored in cookies and then input it.
let TokenType = document.cookie.split("; ").find((row) => row.startsWith(MastodonTokenType + "="))?.split("=")[1]; Notifications = await fetch(Website + "/api/v1/notifications", {method: "GET", headers: {"Authorization": Cookie.MastodonTokenTypeCookie + " " + Cookie.MastodonAccessTokenCookie}})
Notifications = await fetch(Website + "/api/v1/notifications", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
.then((response) => response.json()); .then((response) => response.json());
} }
return Notifications; return Notifications;
} }
// Make a status
export async function CreateStatus(Text) {
// Check for a token
if (Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie)) {
let Website = Cookie.MastodonWebsiteCookie;
return await fetch(Website + "/api/v1/statuses?status=" + Text , {method: "POST", headers: {"Authorization": Cookie.MastodonTokenTypeCookie + " " + Cookie.MastodonAccessTokenCookie}})
.then((response) => response.json());
}
}
// The first step to using the app. // The first step to using the app.
export async function HandleAuthentication(Website, CookieClientID, CookieClientSecret) { export async function HandleAuthentication(Website) {
// See if the user is smart enough to put https. // See if the user is smart enough to put https.
let InstanceData = ""; let InstanceData = "";
// Quickly check to see if it has something before :// so it doesn't screw the link. // Quickly check to see if it has something before :// so it doesn't screw the link.
@ -74,28 +83,30 @@ export async function HandleAuthentication(Website, CookieClientID, CookieClient
} else { } else {
Website = "https://" + Website; Website = "https://" + Website;
} }
// Save the website
Cookie.InputCookie(Cookie.MastodonWebsiteName, Website);
// Registering the app. // Registering the app.
InstanceData = await fetch(Website + "/api/v1/apps?client_name=Channel Viewer&redirect_uris=" + Origin + "&scopes=" + Scopes, {method: "POST"}) InstanceData = await fetch(Website + "/api/v1/apps?client_name=Channel Viewer&redirect_uris=" + document.location.href + "&scopes=" + Scopes, {method: "POST"})
.then((response) => response.json()); .then((response) => response.json());
// Save the client stuff as cookies. // Save the client stuff as cookies.
document.cookie = CookieClientID + "=" + InstanceData.client_id + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; Cookie.InputCookie(Cookie.MastodonClientIDName, InstanceData.client_id);
document.cookie = CookieClientSecret + "=" + InstanceData.client_secret + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; Cookie.InputCookie(Cookie.MastodonClientSecretName, InstanceData.client_secret);
// Now authenticate the app. // Now authenticate the app.
document.location.href = Website + "/oauth/authorize?client_id=" + InstanceData.client_id + "&redirect_uri=" + Origin + "&response_type=code&scope=" + Scopes; document.location.href = Website + "/oauth/authorize?client_id=" + InstanceData.client_id + "&redirect_uri=" + document.location.href + "&response_type=code&scope=" + Scopes;
} }
// This specific functino goes after HandleAuthentication for when login happens. // This specific functino goes after HandleAuthentication for when login happens.
export async function GainToken(Website, CookieClientID, CookieClientSecret, CookieAccessToken, CookieTokenType) { export async function GainToken() {
// check if you both have a code and have a current authentication. // check if you both have a code and have a current authentication.
if (document.location.href.split("code=").length > 1 && document.cookie.split("; ").find((row) => row.startsWith(CookieClientID + "="))?.split("=").length > 1) { if (document.location.href.split("code=").length > 1 && Cookie.IsCookieReal(Cookie.MastodonClientIDCookie) && !(Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie))) {
// Get some vars.
let code = document.location.href.split("code=")[1]; let code = document.location.href.split("code=")[1];
let ClientID = document.cookie.split("; ").find((row) => row.startsWith(CookieClientID + "="))?.split("=")[1]; let Website = Cookie.MastodonWebsiteCookie;
let ClientSecret = document.cookie.split("; ").find((row) => row.startsWith(CookieClientSecret + "="))?.split("=")[1]; // Authenticate.
let AuthenticationToken = await fetch(Website + "/oauth/token?client_id=" + Cookie.MastodonClientIDCookie + "&client_secret=" + Cookie.MastodonClientSecretCookie + "&redirect_uri=" + document.location.href + "&grant_type=authorization_code&code=" + code, {method: "POST"})
let AuthenticationToken = await fetch(Website + "/oauth/token?client_id=" + ClientID + "&client_secret=" + ClientSecret + "&redirect_uri=" + Origin + "&grant_type=authorization_code&code=" + code, {method: "POST"})
.then((response) => response.json()); .then((response) => response.json());
// Cookify These // Cookify These.
document.cookie = CookieAccessToken + "=" + AuthenticationToken.access_token + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; Cookie.InputCookie(Cookie.MastodonAccessTokenName, AuthenticationToken.access_token);
document.cookie = CookieTokenType + "=" + AuthenticationToken.token_type + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; Cookie.InputCookie(Cookie.MastodonTokenTypeName, AuthenticationToken.token_type);
} }
} }

View file

@ -0,0 +1 @@
import * as Cookie from "./Cookies.js";

View file

@ -1,4 +1,7 @@
import * as MastodonAPI from "./MastodonAPI.js"; import * as MastodonAPI from "./MastodonAPI.js";
import * as BlueskyAPI from "./BlueskyAPI.js";
import * as TumblrAPI from "./TumblrAPI.js";
import * as Cookie from "./Cookies.js";
// GLOBAL VARS // GLOBAL VARS
// fuck you. I see why website developers use divs so fucking often. // fuck you. I see why website developers use divs so fucking often.
@ -12,15 +15,13 @@ let BrowserHeight = window.innerHeight;
let ArrowsButton = document.getElementsByClassName("Arrow"); let ArrowsButton = document.getElementsByClassName("Arrow");
let SettingButton = document.getElementsByClassName("Setting")[0]; let SettingButton = document.getElementsByClassName("Setting")[0];
let MailButton = document.getElementsByClassName("Mail")[0]; let MailButton = document.getElementsByClassName("Mail")[0];
let PostingButton = document.getElementsByClassName("Posting")[0];
// Sounds // Sounds
const ButtonSound = new Audio("Audio/button-305770.mp3"); const ButtonSound = new Audio("Audio/button-305770.mp3");
const WarningClick = new Audio("Audio/button-pressed-38129.mp3"); const WarningClick = new Audio("Audio/button-pressed-38129.mp3");
const BackgroundMusic = new Audio("Audio/soft-piano-music-312509.mp3"); const BackgroundMusic = new Audio("Audio/soft-piano-music-312509.mp3");
// Websites
let MastodonWebsite = "https://wetdry.world";
// Update a timer // Update a timer
function UpdateTime() { function UpdateTime() {
let TimeNow = new Date(); let TimeNow = new Date();
@ -104,12 +105,17 @@ ArrowsButton[0].onclick = (event) => {
// MastodonAPI integration // MastodonAPI integration
async function PosterContainerUpdate() { async function PosterContainerUpdate() {
// Cookies for the public timelines // Cookies for the public timelines
let LocalCookie = document.cookie.split("; ").find((row) => row.startsWith("Local="))?.split("=")[1]; let LocalCookie = Cookie.GetCookie("Local");
var LocalTrue = (LocalCookie === 'true'); var LocalTrue = (LocalCookie === "true");
let RemoteCookie = document.cookie.split("; ").find((row) => row.startsWith("Remote="))?.split("=")[1]; let RemoteCookie = Cookie.GetCookie("Remote");
var RemoteTrue = (RemoteCookie === 'true'); var RemoteTrue = (RemoteCookie === "true");
let Website = Cookie.MastodonWebsiteCookie;
let Timeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, MastodonWebsite); if (!(Cookie.IsCookieReal(Cookie.MastodonWebsiteCookie))) {
Website = "https://wetdry.world";
}
let Timeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, Website);
let Content = []; let Content = [];
let Users = []; let Users = [];
for (let i in Timeline) { for (let i in Timeline) {
@ -128,7 +134,12 @@ SettingButton.onclick = (event) => {
window.location.href = "./HTML/setting.html"; window.location.href = "./HTML/setting.html";
} }
// Open the notifs, private message, favorites, bookmarks // Open the notifs, private message, favorites, ... anything mail related!
MailButton.onclick = (event) => { MailButton.onclick = (event) => {
window.location.href = "./HTML/mail.html"; window.location.href = "./HTML/mail.html";
} }
// Open the posting area
PostingButton.onclick = (event) => {
window.location.href = "./HTML/post.html";
}

View file

@ -1,26 +1,11 @@
import * as MastodonAPI from "./MastodonAPI.js"; import * as MastodonAPI from "./MastodonAPI.js";
import * as BlueskyAPI from "./BlueskyAPI.js"; import * as BlueskyAPI from "./BlueskyAPI.js";
import * as TumblrAPI from "./TumblrAPI.js"; import * as TumblrAPI from "./TumblrAPI.js";
import * as Cookie from "./Cookies.js";
// MAKE SURE THESE ARE SYNCED!
// Mastodon
let MastodonWebsite = "https://wetdry.world";
let MastodonClientID = "mastodon_client_id";
let MastodonClientSecret = "mastodon_client_secret";
let MastodonAccessToken = "mastodon_access_token";
let MastodonTokenType = "mastodon_token_type";
// Bluesky
let BlueskyApp = "https://bsky.app";
let BlueskyPDS = "https://bsky.social";
let BlueskyPKCEverifer = "bluesky_pkce_verifier";
let BlueskyPKCEchallenge = "bluesky_pkce_challenge";
let BlueskyPublicKey = "bluesky_public_key";
let BlueskyNonce = "bluesky_nonce";
// Below is the thing it populates if you login. // Below is the thing it populates if you login.
async function PopulateFavorites() { async function PopulateFavorites() {
let Favorites = await MastodonAPI.GetFavorites(MastodonWebsite, MastodonAccessToken, MastodonTokenType); let Favorites = await MastodonAPI.GetFavorites();
let FavoritesArea = document.getElementsByClassName("Favorites")[0]; let FavoritesArea = document.getElementsByClassName("Favorites")[0];
// Populate the favorites area. // Populate the favorites area.
@ -31,7 +16,7 @@ async function PopulateFavorites() {
} }
async function PopulateBookmarks() { async function PopulateBookmarks() {
let Bookmarks = await MastodonAPI.GetBookmarks(MastodonWebsite, MastodonAccessToken, MastodonTokenType); let Bookmarks = await MastodonAPI.GetBookmarks();
let BookmarksArea = document.getElementsByClassName("Bookmarks")[0]; let BookmarksArea = document.getElementsByClassName("Bookmarks")[0];
// Populate the Bookmarks area. // Populate the Bookmarks area.
@ -42,7 +27,7 @@ async function PopulateBookmarks() {
} }
async function PopulateNotifications() { async function PopulateNotifications() {
let Notifications = await MastodonAPI.GetNotifications(MastodonWebsite, MastodonAccessToken, MastodonTokenType); let Notifications = await MastodonAPI.GetNotifications();
let NotificationsArea = document.getElementsByClassName("Notifications")[0]; let NotificationsArea = document.getElementsByClassName("Notifications")[0];
// Populate the Conversations area. // Populate the Conversations area.

27
JS/post.js Normal file
View file

@ -0,0 +1,27 @@
import * as MastodonAPI from "./MastodonAPI.js";
import * as BlueskyAPI from "./BlueskyAPI.js";
import * as TumblrAPI from "./TumblrAPI.js";
import * as Cookie from "./Cookies.js";
// Elements.
let PostButton = document.getElementsByClassName("button")[0];
let InputArea = document.getElementsByClassName("text")[0];
// Clicking the beeg POST button.
PostButton.onclick = (event) => {
Post();
}
async function Post() {
let Text = InputArea.value;
// Mastodon posting.
if (Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie)) {
MastodonAPI.CreateStatus(Text);
}
// Bluesky posting.
if (Cookie.IsCookieReal(Cookie.BlueskyAccessTokenCookie)) {
let DID = await BlueskyAPI.GetBlueskyDID("https://woodear.us-west.host.bsky.network", "crowdedgames.group");
BlueskyAPI.CreatePost("https://woodear.us-west.host.bsky.network", DID.did, Text);
}
InputArea.value = "";
}

View file

@ -1,6 +1,7 @@
import * as MastodonAPI from "./MastodonAPI.js"; import * as MastodonAPI from "./MastodonAPI.js";
import * as BlueskyAPI from "./BlueskyAPI.js"; import * as BlueskyAPI from "./BlueskyAPI.js";
import * as TumblrAPI from "./TumblrAPI.js"; import * as TumblrAPI from "./TumblrAPI.js";
import * as Cookie from "./Cookies.js";
// Settings buttons // Settings buttons
let LocalButton = document.getElementsByClassName("Local")[0]; let LocalButton = document.getElementsByClassName("Local")[0];
@ -13,97 +14,88 @@ let BlueskyLoginButton = document.getElementsByClassName("Login Bluesky")[0];
let BlueskyWebInput = document.getElementsByClassName("WebInput Bluesky")[0]; let BlueskyWebInput = document.getElementsByClassName("WebInput Bluesky")[0];
let BlueskyLogoutButton = document.getElementsByClassName("Logout Bluesky")[0]; let BlueskyLogoutButton = document.getElementsByClassName("Logout Bluesky")[0];
// MAKE SURE THESE ARE SYNCED! // original link
// Mastodon let Origin = location.origin + "/HTML/setting.html"
let MastodonWebsite = "https://wetdry.world";
let MastodonClientID = "mastodon_client_id";
let MastodonClientSecret = "mastodon_client_secret";
let MastodonAccessToken = "mastodon_access_token";
let MastodonTokenType = "mastodon_token_type";
// Bluesky
let BlueskyApp = "https://bsky.app";
let BlueskyPDS = "https://bsky.social";
let BlueskyPKCEverifer = "bluesky_pkce_verifier";
let BlueskyPKCEchallenge = "bluesky_pkce_challenge";
let BlueskyPublicKey = "bluesky_public_key";
let BlueskyNonce = "bluesky_nonce";
let BlueskyAccessToken = "bluesky_access_token";
let BlueskyRefreshToken = "bluesky_refresh_token";
// Tumblr
let TumblrWebsite = "https://www.tumblr.com";
// Change weather the timelines are public or remote // Change weather the timelines are public or remote
LocalButton.onclick = (event) => { LocalButton.onclick = (event) => {
// Toggle the cookie // Toggle the cookie
if (document.cookie.split(";").some((item) => item.trim().startsWith("Local="))) { if (Cookie.IsCookieReal(Cookie.GetCookie("Local"))) {
document.cookie = "Local=true;samesite=strict;path=/;expires=expires=0000-01-01;"; Cookie.ExpireCookie("Local");
} else { } else {
document.cookie = "Local=true;samesite=strict;path=/;"; Cookie.InputCookie("Local", "true");
} }
} }
RemoteButton.onclick = (event) => { RemoteButton.onclick = (event) => {
// Toggle the cookie // Toggle the cookie
if (document.cookie.split(";").some((item) => item.trim().startsWith("Remote="))) { if (Cookie.IsCookieReal(Cookie.GetCookie("Remote"))) {
document.cookie = "Remote=true;samesite=strict;path=/;expires=expires=0000-01-01;"; Cookie.ExpireCookie("Remote");
} else { } else {
document.cookie = "Remote=true;samesite=strict;path=/;"; Cookie.InputCookie("Remote", "true");
} }
} }
// Mastodon buttons // Mastodon buttons
// Login
MastodonLoginButton.onclick = (event) => { MastodonLoginButton.onclick = (event) => {
if (MastodonWebInput != "") { if (MastodonWebInput.value != "") {
MastodonAPI.HandleAuthentication(MastodonWebsite, MastodonClientID, MastodonClientSecret); let text = MastodonWebInput.value
MastodonAPI.HandleAuthentication(text);
} }
} }
// Logout
MastodonLogoutButton.onclick = (event) => { MastodonLogoutButton.onclick = (event) => {
document.cookie = MastodonClientID + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.MastodonClientIDName);
document.cookie = MastodonClientSecret + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.MastodonClientSecretName);
document.cookie = MastodonAccessToken + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.MastodonAccessTokenName);
document.cookie = MastodonTokenType + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.MastodonTokenTypeName);
document.location.href = document.location.href; document.location.href = Origin;
} }
// Bluesky Buttons // Bluesky Buttons
// Login
BlueskyLoginButton.onclick = (event) => { BlueskyLoginButton.onclick = (event) => {
if (BlueskyWebInput != "") { if (BlueskyWebInput.value != "") {
BlueskyAPI.HandleAuthorization(BlueskyPKCEverifer, BlueskyPKCEchallenge, BlueskyNonce); BlueskyAPI.HandleAuthorization();
} }
} }
// Logout
BlueskyLogoutButton.onclick = (event) => { BlueskyLogoutButton.onclick = (event) => {
document.cookie = BlueskyPKCEverifer + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.BlueskyPKCEVeriferName);
document.cookie = BlueskyPKCEchallenge + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.BlueskyPKCEChallengeName);
document.cookie = BlueskyNonce + "=nothing;" + ";samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;"; Cookie.ExpireCookie(Cookie.BlueskyNonceName);
document.location.href = document.location.href; Cookie.ExpireCookie(Cookie.BlueskyAccessTokenName);
Cookie.ExpireCookie(Cookie.BlueskyRefreshTokenName);
Cookie.ExpireCookie(Cookie.BlueskyPublicKeyName);
Cookie.ExpireCookie(Cookie.BlueskyPrivateKeyName);
document.location.href = Origin;
} }
// if an access token is found, login. // if an access token is found, login.
function CheckLogin() { async function CheckLogin() {
// Check for a mastodon token. // Check for a mastodon token.
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1 && document.location.href.split("code=").length == 1) { if (Cookie.IsCookieReal(Cookie.MastodonAccessTokenCookie)) {
// Swap the buttons // Swap the buttons
MastodonLoginButton.remove(); MastodonLoginButton.remove();
MastodonWebInput.remove(); MastodonWebInput.remove();
MastodonLogoutButton.setAttribute("style", ""); MastodonLogoutButton.setAttribute("style", "");
} else { } else {
// Auto log in // Auto log in
MastodonAPI.GainToken(MastodonWebsite, MastodonClientID, MastodonClientSecret, MastodonAccessToken, MastodonTokenType); await MastodonAPI.GainToken(Cookie.MastodonWebsiteName);
} }
if ((document.location.href.split("state=").length == 1 && document.location.href.split("iss=").length == 1 && document.location.href.split("code=").length == 1) && document.cookie.split("; ").find((row) => row.startsWith(BlueskyAccessToken + "="))?.split("=").length > 1) { // Check for a bluesky token.
if (Cookie.IsCookieReal(Cookie.BlueskyAccessTokenCookie)) {
// Swap the buttons // Swap the buttons
BlueskyLoginButton.remove(); BlueskyLoginButton.remove();
BlueskyWebInput.remove(); BlueskyWebInput.remove();
BlueskyLogoutButton.setAttribute("style", ""); BlueskyLogoutButton.setAttribute("style", "");
} else { } else {
// Auto log in // Auto log in
BlueskyAPI.GainTokens(BlueskyPKCEverifer, BlueskyNonce, BlueskyAccessToken, BlueskyRefreshToken); await BlueskyAPI.GainTokens();
} }
// Check for a bluesky token.
} }
// Runs on website start. // Runs on website start.

View file

@ -9,3 +9,9 @@ A frontend to accessing my account on the fediverse
- Your local machine can also run it (with the proper dependencies; take a look at the docker file). - Your local machine can also run it (with the proper dependencies; take a look at the docker file).
Quick launch server without docker: `npx http-server /home/<HomeDirectory>/Documents/Fedi.CrowdedGames.Group --port 4000 --cors` Quick launch server without docker: `npx http-server /home/<HomeDirectory>/Documents/Fedi.CrowdedGames.Group --port 4000 --cors`
## FAQ
|Question|Answer|
|--------|-------|
|Where can I find my PDS?|Create an access token and then examine the payload's `aud`|

View file

@ -292,6 +292,7 @@
<footer class="MainFooter"> <footer class="MainFooter">
<p class="Setting">Setting</p> <p class="Setting">Setting</p>
<p class="Time" style="width: 50%">Time</p> <p class="Time" style="width: 50%">Time</p>
<p class="Posting">Post</p>
<p class="Mail">Mail</p> <p class="Mail">Mail</p>
</footer> </footer>
</section> </section>