Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
a52478da54 | |||
b9f6f45ac5 | |||
ce1c02cd04 | |||
622309c698 | |||
2c643eed9e | |||
723f4c15d7 | |||
2a20fd544e |
11 changed files with 326 additions and 99 deletions
|
@ -6,6 +6,9 @@
|
||||||
<link rel="icon" href="../Icons/favicon.ico" />
|
<link rel="icon" href="../Icons/favicon.ico" />
|
||||||
<link rel="stylesheet" href="../CSS/mail.css">
|
<link rel="stylesheet" href="../CSS/mail.css">
|
||||||
<script type="module" src="../JS/mail.js"></script>
|
<script type="module" src="../JS/mail.js"></script>
|
||||||
|
<!-- Dependenci -->
|
||||||
|
<script language="JavaScript" type="text/javascript" src="https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js">
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="margin: 0px; text-align: center;">
|
<body style="margin: 0px; text-align: center;">
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
export const Scopes = "read write follow push";
|
|
||||||
|
|
||||||
export async function GetPublicTimeline(Local = false, Remote = false) {
|
|
||||||
let Timeline;
|
|
||||||
if (Local == true && Remote == true) {
|
|
||||||
console.error("Don't set both Local and Remote timelines to true.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Local == true) {
|
|
||||||
Timeline = await fetch("https://wetdry.world/api/v1/timelines/public?limit=12&local=true")
|
|
||||||
.then((response) => response.json());
|
|
||||||
} else if (Remote == true) {
|
|
||||||
Timeline = await fetch("https://wetdry.world/api/v1/timelines/public?limit=12&remote=true")
|
|
||||||
.then((response) => response.json());
|
|
||||||
} else {
|
|
||||||
Timeline = await fetch("https://wetdry.world/api/v1/timelines/public?limit=12")
|
|
||||||
.then((response) => response.json());
|
|
||||||
}
|
|
||||||
return Timeline;
|
|
||||||
}
|
|
112
JS/BlueskyAPI.js
Normal file
112
JS/BlueskyAPI.js
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
export async function GetBlueskyDID(PDS, Handle) {
|
||||||
|
let request = fetch(PDS + "/xrpc/com.atproto.identity.resolveDid?handle=" + Handle, { method: "GET"})
|
||||||
|
.then((response) => response.json());
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Component 1/4
|
||||||
|
export async function GetPDSWellKnown() {
|
||||||
|
let Data = await fetch("https://bsky.social/.well-known/oauth-authorization-server", {method: "GET"})
|
||||||
|
.then((response) => response.json());
|
||||||
|
return Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component 2/4
|
||||||
|
// Many thanks to https://github.com/tonyxu-io/pkce-generator. It was the base for this code.
|
||||||
|
export async function CreatePKCECodeVerifier() {
|
||||||
|
// Generate some Numbers
|
||||||
|
let Numbers = new Uint8Array(32);
|
||||||
|
crypto.getRandomValues(Numbers);
|
||||||
|
// Generate a random string of characters.
|
||||||
|
let CodeVerifier = "";
|
||||||
|
for (let i in Numbers) {
|
||||||
|
CodeVerifier += String.fromCharCode(Numbers[i]);
|
||||||
|
}
|
||||||
|
// Put this random string into Base64URL format.
|
||||||
|
CodeVerifier = btoa(CodeVerifier).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
||||||
|
return CodeVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function CreatePKCECodeChallenge(CodeVerifier) {
|
||||||
|
// Generate a code challenge with the code verifier.
|
||||||
|
// This is done by first SHA256 encrypting the CodeVerifier, then putting the outputted string into Base64URL format.
|
||||||
|
let CodeChallenge = btoa(await sha256(CodeVerifier)).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
||||||
|
return CodeChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component 3/4
|
||||||
|
export async function CreatePAR() {
|
||||||
|
let WellKnown = await GetPDSWellKnown();
|
||||||
|
// Some verification mechanism with PAR
|
||||||
|
let PAREndpoint = WellKnown.pushed_authorization_request_endpoint;
|
||||||
|
|
||||||
|
|
||||||
|
// Token goes into the authorization endpoint
|
||||||
|
let AuthEndpoint = WellKnown.authorization_endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component 4/4
|
||||||
|
export async function ClientDPoP() {
|
||||||
|
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
var Header = {alg: "HS256", typ: "dpop+jwt", jwk: await crypto.subtle.exportKey("jwk", KeyPair.publicKey).then(function(response) {return response})};
|
||||||
|
// Payload
|
||||||
|
var Payload = {};
|
||||||
|
// Payload.jti
|
||||||
|
// Payload.htm
|
||||||
|
// Payload.htu
|
||||||
|
Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000);
|
||||||
|
// Payload.nonce
|
||||||
|
|
||||||
|
var sHeader = JSON.stringify(Header);
|
||||||
|
var sPayload = JSON.stringify(Payload);
|
||||||
|
var JWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "616161");
|
||||||
|
console.log(JWT);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ServerDPoP() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AssertionJWT(BlueskyClientID) {
|
||||||
|
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
var Header = {alg: "HS256", kid: await crypto.subtle.exportKey("jwk", KeyPair.publicKey).then(function(response) {return response})};
|
||||||
|
// Payload
|
||||||
|
var Payload = {};
|
||||||
|
|
||||||
|
Payload.iss = BlueskyClientID;
|
||||||
|
Payload.sub = BlueskyClientID;
|
||||||
|
// Payload.aud
|
||||||
|
// Payload.jti
|
||||||
|
Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000);
|
||||||
|
|
||||||
|
var sHeader = JSON.stringify(Header);
|
||||||
|
var sPayload = JSON.stringify(Payload);
|
||||||
|
var JWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "838383");
|
||||||
|
console.log(JWT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stolen from elsewhere.
|
||||||
|
// Firefox snippet; Slightly edited.
|
||||||
|
async function sha256(message) {
|
||||||
|
// encode as UTF-8
|
||||||
|
const MessageBuffer = new TextEncoder().encode(message);
|
||||||
|
|
||||||
|
// hash the message
|
||||||
|
const HashBuffer = await crypto.subtle.digest('SHA-256', MessageBuffer);
|
||||||
|
|
||||||
|
// convert ArrayBuffer to Array
|
||||||
|
const HashArray = Array.from(new Uint8Array(HashBuffer));
|
||||||
|
|
||||||
|
// convert this hashArray to a string
|
||||||
|
let string = "";
|
||||||
|
for (let i in HashArray) {
|
||||||
|
string += String.fromCharCode(HashArray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
101
JS/MastodonAPI.js
Normal file
101
JS/MastodonAPI.js
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
export const Scopes = "read write follow push";
|
||||||
|
|
||||||
|
// Gets the public timeline.
|
||||||
|
export async function GetPublicTimeline(Local = false, Remote = false, Website) {
|
||||||
|
let Timeline;
|
||||||
|
if (Local == true && Remote == true) {
|
||||||
|
console.error("Don't set both Local and Remote timelines to true.");
|
||||||
|
return Timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Local == true) {
|
||||||
|
Timeline = await fetch(Website + "/api/v1/timelines/public?limit=12&local=true")
|
||||||
|
.then((response) => response.json());
|
||||||
|
} else if (Remote == true) {
|
||||||
|
Timeline = await fetch(Website + "/api/v1/timelines/public?limit=12&remote=true")
|
||||||
|
.then((response) => response.json());
|
||||||
|
} else {
|
||||||
|
Timeline = await fetch(Website + "/api/v1/timelines/public?limit=12")
|
||||||
|
.then((response) => response.json());
|
||||||
|
}
|
||||||
|
return Timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the favorites of a user that you have access to.
|
||||||
|
export async function GetFavorites(Website, MastodonAccessToken, MastodonTokenType) {
|
||||||
|
let Favorites;
|
||||||
|
// Check for a token.
|
||||||
|
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) {
|
||||||
|
// Get the varaibles that are stored in cookies.
|
||||||
|
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=")[1];
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
return Favorites;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the bookmarks of a user that you have access to.
|
||||||
|
export async function GetBookmarks(Website, MastodonAccessToken, MastodonTokenType) {
|
||||||
|
let Bookmarks;
|
||||||
|
// Check for a token.
|
||||||
|
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) {
|
||||||
|
// Get the varaibles that are stored in cookies.
|
||||||
|
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=")[1];
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
return Bookmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the notifications of a user that you have access to.
|
||||||
|
export async function GetNotifications(Website, MastodonAccessToken, MastodonTokenType) {
|
||||||
|
let Notifications;
|
||||||
|
// Check for a token.
|
||||||
|
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) {
|
||||||
|
// Get the varaibles that are stored in cookies.
|
||||||
|
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=")[1];
|
||||||
|
let TokenType = document.cookie.split("; ").find((row) => row.startsWith(MastodonTokenType + "="))?.split("=")[1];
|
||||||
|
Notifications = await fetch(Website + "/api/v1/notifications", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
|
||||||
|
.then((response) => response.json());
|
||||||
|
}
|
||||||
|
return Notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first step to using the app.
|
||||||
|
export async function HandleAuthentication(Website, CookieClientID, CookieClientSecret) {
|
||||||
|
// See if the user is smart enough to put https.
|
||||||
|
let InstanceData;
|
||||||
|
if (!Website.toLowerCase().split("://")[0].includes("https")) {
|
||||||
|
// Quickly check to see if it has something before :// so it doesn't screw the link.
|
||||||
|
if (Website.toLowerCase().split("://").length > 1) {
|
||||||
|
Website = "https://" + Website.split("://")[1];
|
||||||
|
} else {
|
||||||
|
Website = "https://" + Website;
|
||||||
|
}
|
||||||
|
InstanceData = await fetch(Website + "/api/v1/apps?client_name=Channel Viewer&redirect_uris=" + Origin + "&scopes=" + ActivityPub.Scopes, {method: "POST"})
|
||||||
|
.then((response) => response.json());
|
||||||
|
}
|
||||||
|
// Save the client stuff as cookies.
|
||||||
|
document.cookie = CookieClientID + "=" + InstanceData.client_id + ";samesite=strict;path=/;expires=9999-01-01;";
|
||||||
|
document.cookie = CookieClientSecret + "=" + InstanceData.client_secret + ";samesite=strict;path=/;expires=9999-01-01;";
|
||||||
|
// Now authenticate the app.
|
||||||
|
document.location.href = Website + "/oauth/authorize?client_id=" + InstanceData.client_id + "&redirect_uri=" + Origin + "&response_type=code&scope=" + ActivityPub.Scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This specific functino goes after HandleAuthentication for when login happens.
|
||||||
|
export async function GainToken(Website, CookieClientID, CookieClientSecret, CookieAccessToken, CookieTokenType) {
|
||||||
|
// 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("=") > 1) {
|
||||||
|
let code = document.location.href.split("code=")[1];
|
||||||
|
let ClientID = document.cookie.split("; ").find((row) => row.startsWith(CookieClientID + "="))?.split("=")[1];
|
||||||
|
let ClientSecret = document.cookie.split("; ").find((row) => row.startsWith(CookieClientSecret + "="))?.split("=")[1];
|
||||||
|
|
||||||
|
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());
|
||||||
|
// Cookify These
|
||||||
|
document.cookie = CookieAccessToken + "=" + AuthenticationToken.access_token + ";samesite=strict;path=/;expires=9999-01-01;";
|
||||||
|
document.cookie = CookieTokenType + "=" + AuthenticationToken.token_type + ";samesite=strict;path=/;expires=9999-01-01;";
|
||||||
|
}
|
||||||
|
}
|
0
JS/TumblrAPI.js
Normal file
0
JS/TumblrAPI.js
Normal file
|
@ -1,4 +1,4 @@
|
||||||
import * as ActivityPub from "./ActivityPub.js";
|
import * as MastodonAPI from "./MastodonAPI.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.
|
||||||
|
@ -18,6 +18,9 @@ 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();
|
||||||
|
@ -98,7 +101,7 @@ ArrowsButton[0].onclick = (event) => {
|
||||||
PosterContainerUpdate();
|
PosterContainerUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActivityPub 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 = document.cookie.split("; ").find((row) => row.startsWith("Local="))?.split("=")[1];
|
||||||
|
@ -106,7 +109,7 @@ async function PosterContainerUpdate() {
|
||||||
let RemoteCookie = document.cookie.split("; ").find((row) => row.startsWith("Remote="))?.split("=")[1];
|
let RemoteCookie = document.cookie.split("; ").find((row) => row.startsWith("Remote="))?.split("=")[1];
|
||||||
var RemoteTrue = (RemoteCookie === 'true');
|
var RemoteTrue = (RemoteCookie === 'true');
|
||||||
|
|
||||||
let Timeline = await ActivityPub.GetPublicTimeline(LocalTrue, RemoteTrue);
|
let Timeline = await MastodonAPI.GetPublicTimeline(LocalTrue, RemoteTrue, MastodonWebsite);
|
||||||
let Content = [];
|
let Content = [];
|
||||||
let Users = [];
|
let Users = [];
|
||||||
for (let i in Timeline) {
|
for (let i in Timeline) {
|
||||||
|
|
150
JS/mail.js
150
JS/mail.js
|
@ -1,47 +1,39 @@
|
||||||
import * as ActivityPub from "./ActivityPub.js";
|
import * as MastodonAPI from "./MastodonAPI.js";
|
||||||
|
import * as BlueskyAPI from "./BlueskyAPI.js";
|
||||||
|
import * as TumblrAPI from "./TumblrAPI.js";
|
||||||
|
|
||||||
let LoginButton = document.getElementsByClassName("Login")[0];
|
let LoginButton = document.getElementsByClassName("Login")[0];
|
||||||
let LogoutButton = document.getElementsByClassName("Logout")[0];
|
let LogoutButton = document.getElementsByClassName("Logout")[0];
|
||||||
let Origin = window.location.origin + "/HTML/mail"
|
let Origin = window.location.origin + "/HTML/mail"
|
||||||
|
|
||||||
|
// Mastodon
|
||||||
|
let MastodonWebsite = "https://wetdry.world";
|
||||||
|
let MastodonClientID = "mastodon_client_id";
|
||||||
|
let MastodonClientSecret = "mastodon_client_secret";
|
||||||
|
let MastodonAccessToken = "mastodon_access_token";
|
||||||
|
let MastodonTokenType = "mastodon_access_token";
|
||||||
|
|
||||||
|
// Bluesky (TODO: use these variables).
|
||||||
|
let BlueskyApp = "https://bsky.app";
|
||||||
|
let BlueskyPDS = "https://bsky.social";
|
||||||
|
|
||||||
|
// Tumblr
|
||||||
|
let TumblrWebsite = "https://www.tumblr.com";
|
||||||
|
|
||||||
LoginButton.onclick = (event) => {
|
LoginButton.onclick = (event) => {
|
||||||
HandleAuthentication();
|
MastodonAPI.HandleAuthentication(MastodonWebsite, MastodonClientID, MastodonClientSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogoutButton.onclick = (event) => {
|
LogoutButton.onclick = (event) => {
|
||||||
console.log("Does nothing so far.");
|
document.cookie = MastodonAccessToken + "=nothing;" + ";samesite=strict;path=/;expires=0000-01-01;";
|
||||||
}
|
document.cookie = MastodonTokenType + "=nothing;" + ";samesite=strict;path=/;expires=0000-01-01;";
|
||||||
|
console.log("Cleared the access token.");
|
||||||
async function HandleAuthentication() {
|
|
||||||
let InstanceData = await fetch("https://wetdry.world/api/v1/apps?client_name=Channel Viewer&redirect_uris=" + Origin + "&scopes=" + ActivityPub.Scopes, {method: "POST"})
|
|
||||||
.then((response) => response.json());
|
|
||||||
// Save the client stuff as cookies (STRICTLY THIS SITE!).
|
|
||||||
document.cookie = "client_id=" + InstanceData.client_id + ";samesite=strict;path=/;expires=Thu, 01 Jan 9999 00:00:00 GMT;";
|
|
||||||
document.cookie = "client_secret=" + InstanceData.client_secret + ";samesite=strict;path=/;expires=Thu, 01 Jan 9999 00:00:00 GMT;";
|
|
||||||
// Now authenticate the app.
|
|
||||||
let InstanceInfo = await fetch("https://wetdry.world/api/v1/instance", {method: "GET"})
|
|
||||||
.then((response) => response.json());
|
|
||||||
document.location.href = "https://wetdry.world/oauth/authorize?client_id=" + InstanceData.client_id + "&redirect_uri=" + Origin + "&response_type=code&scope=" + ActivityPub.Scopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the website starts, check to see if you actually went and got a code.
|
|
||||||
async function HandleLogin() {
|
|
||||||
if (document.location.href.split("code=").length > 1 && document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=") > 1) {
|
|
||||||
let code = document.location.href.split("code=")[1];
|
|
||||||
let ClientID = document.cookie.split("; ").find((row) => row.startsWith("client_id="))?.split("=")[1];
|
|
||||||
let ClientSecret = document.cookie.split("; ").find((row) => row.startsWith("client_secret="))?.split("=")[1];
|
|
||||||
|
|
||||||
let AuthenticationToken = await fetch("https://wetdry.world/oauth/token?client_id=" + ClientID + "&client_secret=" + ClientSecret + "&redirect_uri=" + Origin + "&grant_type=authorization_code&code=" + code, {method: "POST"})
|
|
||||||
.then((response) => response.json());
|
|
||||||
// Cookify These
|
|
||||||
document.cookie = "access_token=" + AuthenticationToken.access_token + ";samesite=strict;path=/;expires=Thu, 01 Jan 9999 00:00:00 GMT;";
|
|
||||||
document.cookie = "token_type=" + AuthenticationToken.token_type + ";samesite=strict;path=/;expires=Thu, 01 Jan 9999 00:00:00 GMT;";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if an access token is found, login.
|
||||||
function CheckLogin() {
|
function CheckLogin() {
|
||||||
// Check for a token.
|
// Check for a token.
|
||||||
if (document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=").length > 1) {
|
if (document.cookie.split("; ").find((row) => row.startsWith(MastodonAccessToken + "="))?.split("=").length > 1) {
|
||||||
// Swap the buttons
|
// Swap the buttons
|
||||||
LoginButton.remove();
|
LoginButton.remove();
|
||||||
LogoutButton.setAttribute("style", "");
|
LogoutButton.setAttribute("style", "");
|
||||||
|
@ -50,54 +42,35 @@ function CheckLogin() {
|
||||||
|
|
||||||
// Below is the thing it populates if you login.
|
// Below is the thing it populates if you login.
|
||||||
async function PopulateFavorites() {
|
async function PopulateFavorites() {
|
||||||
// Check for a token.
|
let Favorites = MastodonAPI.GetFavorites(MastodonWebsite, MastodonAccessToken, MastodonTokenType);
|
||||||
if (document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=").length > 1) {
|
|
||||||
// Get the varaibles that are stored in cookies.
|
let FavoritesArea = document.getElementsByClassName("Favorites")[0];
|
||||||
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=")[1];
|
// Populate the favorites area.
|
||||||
let TokenType = document.cookie.split("; ").find((row) => row.startsWith("token_type="))?.split("=")[1];
|
for (let i in Favorites) {
|
||||||
let Favorites = await fetch("https://wetdry.world/api/v1/favourites", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
|
FavoritesArea.innerHTML += "<article style='top:" + getRandomArbitrary(0, 84) + "%; left: " + getRandomArbitrary(0, 84) + "%;' class='Favorite'></article>";
|
||||||
.then((response) => response.json());
|
FavoritesArea.getElementsByClassName("Favorite")[i].innerHTML = Favorites[i].content;
|
||||||
let FavoritesArea = document.getElementsByClassName("Favorites")[0];
|
|
||||||
// Populate the favorites area.
|
|
||||||
for (let i in Favorites) {
|
|
||||||
FavoritesArea.innerHTML += "<article style='top:" + getRandomArbitrary(0, 84) + "%; left: " + getRandomArbitrary(0, 84) + "%;' class='Favorite'></article>";
|
|
||||||
FavoritesArea.getElementsByClassName("Favorite")[i].innerHTML = Favorites[i].content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function PopulateBookmarks() {
|
async function PopulateBookmarks() {
|
||||||
// Check for a token.
|
let Bookmarks = MastodonAPI.GetBookmarks(MastodonWebsite, MastodonAccessToken, MastodonTokenType);
|
||||||
if (document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=").length > 1) {
|
|
||||||
// Get the varaibles that are stored in cookies.
|
let BookmarksArea = document.getElementsByClassName("Bookmarks")[0];
|
||||||
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=")[1];
|
// Populate the Bookmarks area.
|
||||||
let TokenType = document.cookie.split("; ").find((row) => row.startsWith("token_type="))?.split("=")[1];
|
for (let i in Bookmarks) {
|
||||||
let Bookmarks = await fetch("https://wetdry.world/api/v1/bookmarks", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
|
BookmarksArea.innerHTML += "<article style='top:" + getRandomArbitrary(0, 84) + "%; left: " + getRandomArbitrary(0, 84) + "%;' class='Bookmark'></article>";
|
||||||
.then((response) => response.json());
|
BookmarksArea.getElementsByClassName("Bookmark")[i].innerHTML = Bookmarks[i].content;
|
||||||
let BookmarksArea = document.getElementsByClassName("Bookmarks")[0];
|
|
||||||
// Populate the Bookmarks area.
|
|
||||||
for (let i in Bookmarks) {
|
|
||||||
BookmarksArea.innerHTML += "<article style='top:" + getRandomArbitrary(0, 84) + "%; left: " + getRandomArbitrary(0, 84) + "%;' class='Bookmark'></article>";
|
|
||||||
BookmarksArea.getElementsByClassName("Bookmark")[i].innerHTML = Bookmarks[i].content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function PopulateNotifications() {
|
async function PopulateNotifications() {
|
||||||
// Check for a token.
|
let Notifications = MastodonAPI.GetNotifications(MastodonWebsite, MastodonAccessToken, MastodonTokenType);
|
||||||
if (document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=").length > 1) {
|
|
||||||
// Get the varaibles that are stored in cookies.
|
let NotificationsArea = document.getElementsByClassName("Notifications")[0];
|
||||||
let AccessToken = document.cookie.split("; ").find((row) => row.startsWith("access_token="))?.split("=")[1];
|
// Populate the Conversations area.
|
||||||
let TokenType = document.cookie.split("; ").find((row) => row.startsWith("token_type="))?.split("=")[1];
|
for (let i in Notifications) {
|
||||||
let Notifications = await fetch("https://wetdry.world/api/v1/notifications", {method: "GET", headers: {"Authorization": TokenType + " " + AccessToken}})
|
NotificationsArea.innerHTML += "<article style='top:" + getRandomArbitrary(0, 84) + "%; left: " + getRandomArbitrary(0, 84) + "%;' class='Notification'></article>";
|
||||||
.then((response) => response.json());
|
NotificationsArea.getElementsByClassName("Notification")[i].innerHTML = Notifications[i].status.content;
|
||||||
console.log(Notifications);
|
|
||||||
let NotificationsArea = document.getElementsByClassName("Notifications")[0];
|
|
||||||
// Populate the Conversations area.
|
|
||||||
for (let i in Notifications) {
|
|
||||||
NotificationsArea.innerHTML += "<article style='top:" + getRandomArbitrary(0, 84) + "%; left: " + getRandomArbitrary(0, 84) + "%;' class='Notification'></article>";
|
|
||||||
NotificationsArea.getElementsByClassName("Notification")[i].innerHTML = Notifications[i].status.content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +78,8 @@ async function PopulateNotifications() {
|
||||||
// Remove traces of "login".
|
// Remove traces of "login".
|
||||||
CheckLogin();
|
CheckLogin();
|
||||||
|
|
||||||
// Authentication.
|
// So far: login. TODO: Change this later.
|
||||||
HandleLogin();
|
MastodonAPI.GainToken(MastodonWebsite, MastodonClientID, MastodonClientSecret, MastodonAccessToken, MastodonTokenType);
|
||||||
|
|
||||||
// Populate the areas.
|
// Populate the areas.
|
||||||
PopulateFavorites();
|
PopulateFavorites();
|
||||||
|
@ -118,3 +91,34 @@ PopulateNotifications();
|
||||||
function getRandomArbitrary(min, max) {
|
function getRandomArbitrary(min, max) {
|
||||||
return Math.random() * (max - min) + min;
|
return Math.random() * (max - min) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The next section is dedicated to testing.
|
||||||
|
// WARNING: I don't know what I am doing.
|
||||||
|
BlueskyAPI.ClientDPoP();
|
||||||
|
BlueskyAPI.AssertionJWT("Nothing");
|
||||||
|
|
||||||
|
async function BlueskyTestingAuthorization() {
|
||||||
|
let WellKnown = await BlueskyAPI.GetPDSWellKnown();
|
||||||
|
let PAREndpoint = WellKnown.pushed_authorization_request_endpoint;
|
||||||
|
|
||||||
|
let TestingState = generateToken(64);
|
||||||
|
|
||||||
|
let TestingVerifier = await BlueskyAPI.CreatePKCECodeVerifier()
|
||||||
|
let TestingChallenge = await BlueskyAPI.CreatePKCECodeChallenge(TestingVerifier);
|
||||||
|
|
||||||
|
let TestingRequest = fetch(PAREndpoint + "?state=" + TestingState + "&pkceChallenge=" + TestingChallenge + "&scopes=atproto&login_hint=crowdedgames.group", {method: "POST"});
|
||||||
|
console.log(TestingRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlueskyTestingAuthorization();
|
||||||
|
|
||||||
|
// Stolen from Brave
|
||||||
|
// TODO: implement my own function.
|
||||||
|
function generateToken(length) {
|
||||||
|
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
var token = '';
|
||||||
|
for(var i = 0; i < length; i++) {
|
||||||
|
token += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ let RemoteButton = document.getElementsByClassName("Remote")[0];
|
||||||
LocalButton.onclick = (event) => {
|
LocalButton.onclick = (event) => {
|
||||||
// Toggle the cookie
|
// Toggle the cookie
|
||||||
if (document.cookie.split(";").some((item) => item.trim().startsWith("Local="))) {
|
if (document.cookie.split(";").some((item) => item.trim().startsWith("Local="))) {
|
||||||
document.cookie = "Local=true;samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;";
|
document.cookie = "Local=true;samesite=strict;path=/;expires=expires=0000-01-01;";
|
||||||
} else {
|
} else {
|
||||||
document.cookie = "Local=true;samesite=strict;path=/;";
|
document.cookie = "Local=true;samesite=strict;path=/;";
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ LocalButton.onclick = (event) => {
|
||||||
RemoteButton.onclick = (event) => {
|
RemoteButton.onclick = (event) => {
|
||||||
// Toggle the cookie
|
// Toggle the cookie
|
||||||
if (document.cookie.split(";").some((item) => item.trim().startsWith("Remote="))) {
|
if (document.cookie.split(";").some((item) => item.trim().startsWith("Remote="))) {
|
||||||
document.cookie = "Remote=true;samesite=strict;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT;";
|
document.cookie = "Remote=true;samesite=strict;path=/;expires=expires=0000-01-01;";
|
||||||
} else {
|
} else {
|
||||||
document.cookie = "Remote=true;samesite=strict;path=/;";
|
document.cookie = "Remote=true;samesite=strict;path=/;";
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,7 @@ A frontend to accessing my account on the fediverse
|
||||||
## technologies
|
## technologies
|
||||||
- node on a bare debian install.
|
- node on a bare debian install.
|
||||||
- npm and npx to execute it.
|
- npm and npx to execute it.
|
||||||
- currently only uses the Mastodon V1 API.
|
- Uses MastodonAPI, BlueskyAPI, and TumblrAPI.
|
||||||
|
- 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`
|
||||||
|
|
3
oauth/README.md
Normal file
3
oauth/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Bluesky stuff.
|
||||||
|
|
||||||
|
Yeah.
|
18
oauth/client-metadata.json
Normal file
18
oauth/client-metadata.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"client_id": "https://fedi.crowdedgames.group/oauth/client-metadata.json",
|
||||||
|
"application_type": "web",
|
||||||
|
"client_name": "Example Browser App",
|
||||||
|
"client_uri": "https://fedi.crowdedgames.group",
|
||||||
|
"dpop_bound_access_tokens": true,
|
||||||
|
"grant_types": [
|
||||||
|
"authorization_code",
|
||||||
|
"refresh_token"
|
||||||
|
],
|
||||||
|
"redirect_uris": [
|
||||||
|
"https://fedi.crowdedgames.group/HTML/mail.html"
|
||||||
|
],
|
||||||
|
"response_types": [
|
||||||
|
"code"
|
||||||
|
],
|
||||||
|
"scope": "atproto transition:generic",
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue