data endpoint for entity 90008 (aka. a website)

feat: keep track of global distance moved and show some stats for pet

ptr.pet 5ad4b2ee cb01edcd

verified
Changed files
+137 -47
src
+1
.gitignore
···
/bouncecount
/legitvisitcount
/fakevisitcount
+
/distancetravelled
/notes
/note
+39 -7
src/components/pet.svelte
···
+
<script module lang="ts">
+
import { writable } from 'svelte/store';
+
+
export const localDistanceTravelled = writable(0.0);
+
</script>
+
<script lang="ts">
import { draggable } from '@neodrag/svelte';
+
import { browser } from '$app/environment';
let lastDragged = 0;
let mouseX = 0;
···
};
// Add spring update to the move function
-
setInterval(updateRotationSpring, tickRate);
+
if (browser) setInterval(updateRotationSpring, tickRate);
const moveTowards = (from: number, to: number, by: number) => {
let d = (to - from) * 1.0;
···
fetch('/pet/bounce');
};
+
let deltaTravelled = 0.0;
+
const updateDistanceTravelled = () => {
+
if (deltaTravelled > 0.1 || deltaTravelled < -0.1) {
+
localDistanceTravelled.update((n) => {
+
n += deltaTravelled;
+
return n;
+
});
+
fetch('/pet/distance', {
+
method: 'POST',
+
body: deltaTravelled.toString()
+
});
+
}
+
deltaTravelled = 0.0;
+
};
+
const move = () => {
if (dragged) return;
···
velocityY *= fric;
// Update position
-
position.x += velocityX * delta;
-
position.y += velocityY * delta;
+
const moveX = velocityX * delta;
+
const moveY = velocityY * delta;
+
position.x += moveX;
+
position.y += moveY;
+
+
deltaTravelled += Math.sqrt(moveX ** 2 + moveY ** 2);
+
updateDistanceTravelled();
// Handle window boundaries
const viewportWidth = window.innerWidth;
···
} else {
sprite = '/pet/idle.webp';
}
+
+
deltaTravelled += Math.abs(moveByX);
+
updateDistanceTravelled();
};
-
setInterval(move, tickRate);
+
if (browser) setInterval(move, tickRate);
const shake = (event: DeviceMotionEvent) => {
const accel = event.acceleration ?? event.accelerationIncludingGravity;
···
sprite = '/pet/pick.webp';
};
-
self.ondevicemotion = shake;
+
if (browser) self.ondevicemotion = shake;
// this is for ios
const askForShakePermission = () => {
···
};
// Start the process
-
setTimeout(pickNewTargetX, 1000);
+
if (browser) setTimeout(pickNewTargetX, 1000);
</script>
<!-- svelte-ignore a11y_missing_attribute -->
···
position.y = offsetY;
const mouseXD = event.movementX * delta;
const mouseYD = event.movementY * delta;
+
deltaTravelled += Math.sqrt(mouseXD ** 2 + mouseYD ** 2);
// reset mouse movement if it's not moving in the same direction so it doesnt accumulate its weird!@!@
mouseX = Math.sign(mouseXD) != Math.sign(mouseX) ? mouseXD : mouseX + mouseXD;
mouseY = Math.sign(mouseYD) != Math.sign(mouseY) ? mouseYD : mouseY + mouseYD;
···
lastDragged = Date.now();
},
onDragEnd: () => {
-
dragged = false;
// reset mouse movement if we stopped for longer than some time
if (Date.now() - lastDragged > 50) {
mouseX = 0.0;
···
// apply velocity based on rotation since we already keep track of that
velocityX = mouseX * 70.0;
velocityY = mouseY * 50.0;
+
updateDistanceTravelled();
// reset mouse movement we dont want it to accumulate
mouseX = 0.0;
mouseY = 0.0;
+
dragged = false;
}
}}
class="fixed bottom-[5vh] z-[1000] hover:animate-squiggle"
+33
src/lib/counter.ts
···
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
+
import { get, writable } from 'svelte/store';
+
+
/**
+
* Creates a persistent counter that is stored in a file
+
* @param fileName The name of the file to store the count in
+
* @param initialValue The initial value if the file doesn't exist
+
* @returns An object with methods to get, increment, and set the count
+
*/
+
export const createFileCounter = (filePath: string, initialValue: number = 0) => {
+
const counter = writable(
+
parseInt(existsSync(filePath) ? readFileSync(filePath).toString() : initialValue.toString())
+
);
+
+
const saveToFile = (value: number) => {
+
writeFileSync(filePath, value.toString());
+
return value;
+
};
+
+
return {
+
get: () => get(counter),
+
increment: (amount: number = 1) => {
+
const currentValue = get(counter) + amount;
+
counter.set(currentValue);
+
return saveToFile(currentValue);
+
},
+
set: (value: number) => {
+
counter.set(value);
+
return saveToFile(value);
+
},
+
subscribe: counter.subscribe
+
};
+
};
+8 -38
src/lib/metrics.ts
···
import { env } from '$env/dynamic/private';
-
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { pushMetrics } from 'prometheus-remote-write';
-
import { get, writable } from 'svelte/store';
+
import { createFileCounter } from './counter';
const endpoint = env.PROMETHEUS_URL;
···
await pushMetric({
gazesys_pet_bounce_total: bounceCount.get(),
gazesys_visit_fake_total: fakeVisitCount.get(),
-
gazesys_visit_real_total: legitVisitCount.get()
+
gazesys_visit_real_total: legitVisitCount.get(),
+
gazesys_pet_distance_total: distanceTravelled.get()
});
} catch (error) {
console.log(`failed to push metrics: ${error}`);
}
};
-
/**
-
* Creates a persistent counter that is stored in a file
-
* @param fileName The name of the file to store the count in
-
* @param initialValue The initial value if the file doesn't exist
-
* @returns An object with methods to get, increment, and set the count
-
*/
-
export const createFileCounter = (fileName: string, initialValue: number = 0) => {
-
const filePath = `${env.WEBSITE_DATA_DIR}/${fileName}`;
-
const counter = writable(
-
parseInt(existsSync(filePath) ? readFileSync(filePath).toString() : initialValue.toString())
-
);
-
-
const saveToFile = (value: number) => {
-
writeFileSync(filePath, value.toString());
-
return value;
-
};
-
-
return {
-
get: () => get(counter),
-
increment: (amount: number = 1) => {
-
const currentValue = get(counter) + amount;
-
counter.set(currentValue);
-
return saveToFile(currentValue);
-
},
-
set: (value: number) => {
-
counter.set(value);
-
return saveToFile(value);
-
},
-
subscribe: counter.subscribe
-
};
-
};
-
-
export const bounceCount = createFileCounter('bouncecount');
+
export const bounceCount = createFileCounter(`${env.WEBSITE_DATA_DIR}/bouncecount`);
export const incrementBounceCount = bounceCount.increment;
-
export const legitVisitCount = createFileCounter('legitvisitcount');
+
export const legitVisitCount = createFileCounter(`${env.WEBSITE_DATA_DIR}/legitvisitcount`);
export const incrementLegitVisitCount = legitVisitCount.increment;
-
export const fakeVisitCount = createFileCounter('fakevisitcount');
+
export const fakeVisitCount = createFileCounter(`${env.WEBSITE_DATA_DIR}/fakevisitcount`);
export const incrementFakeVisitCount = fakeVisitCount.increment;
+
+
export const distanceTravelled = createFileCounter(`${env.WEBSITE_DATA_DIR}/distancetravelled`);
+12 -1
src/routes/+layout.server.ts
···
-
import { incrementFakeVisitCount, incrementLegitVisitCount, pushMetric } from '$lib/metrics.js';
+
import {
+
bounceCount,
+
distanceTravelled,
+
incrementFakeVisitCount,
+
incrementLegitVisitCount,
+
pushMetric
+
} from '$lib/metrics.js';
import { testUa } from '$lib/robots.js';
import { addLastVisitor, incrementVisitCount, notifyDarkVisitors } from '$lib/visits.js';
import { error } from '@sveltejs/kit';
+
import { localDistanceTravelled } from '../components/pet.svelte';
+
import { get } from 'svelte/store';
export const csr = true;
export const ssr = true;
···
return {
route: url.pathname,
+
petTotalBounce: bounceCount.get(),
+
petTotalDistance: distanceTravelled.get(),
+
petLocalDistance: get(localDistanceTravelled),
visitCount: incrementVisitCount(request, cookies),
lastVisitors,
recentVisitCount
+31 -1
src/routes/+layout.svelte
···
<script lang="ts">
+
import { browser } from '$app/environment';
import getTitle from '$lib/getTitle';
import NavButton from '../components/navButton.svelte';
-
import Pet from '../components/pet.svelte';
+
import Pet, { localDistanceTravelled } from '../components/pet.svelte';
import Tooltip from '../components/tooltip.svelte';
import '../styles/app.css';
···
<a class="align-middle hover:underline" href="/log/_rss">log</a>
</div>
{/if}
+
<Tooltip>
+
{#snippet tooltipContent()}
+
<p class="font-monospace">
+
<nobr>
+
pet global bounce = <span class="text-ralsei-green-light text-shadow-green"
+
>{data.petTotalBounce.toString().padStart(14, '.')}</span
+
>
+
</nobr>
+
<nobr>
+
pet global distance = <span class="text-ralsei-green-light text-shadow-green"
+
>{data.petTotalDistance.toFixed(0).toString().padStart(12, '.')}</span
+
>
+
</nobr>
+
{#if browser}
+
<nobr>
+
pet local distance = <span class="text-ralsei-green-light text-shadow-green"
+
>{$localDistanceTravelled.toFixed(0).toString().padStart(13, '.')}</span
+
>
+
</nobr>
+
{/if}
+
</p>
+
{/snippet}
+
<div class="navbox">
+
<p>
+
<span class="text-ralsei-green-light text-shadow-green">*</span>
+
pet stats
+
</p>
+
</div>
+
</Tooltip>
<Tooltip>
{#snippet tooltipContent()}
<p class="font-monospace">
+13
src/routes/pet/distance/+server.ts
···
+
import { distanceTravelled, pushMetric } from '$lib/metrics';
+
import { isBot } from '$lib/visits';
+
+
export const POST = async ({ request }) => {
+
if (isBot(request)) return new Response();
+
try {
+
const delta = parseFloat(await request.text());
+
await pushMetric({ gazesys_pet_distance_total: distanceTravelled.increment(delta) });
+
} catch (error) {
+
console.log(`error while pushing bounce metric: ${error}`);
+
}
+
return new Response();
+
};