Search
isMyWebsiteDown
@willthereader
Uptime Checker & Status Page Installation Fork this val Edit the list of URLs to what you want to check For the status page, fork this val: @stevekrouse/status
Script
await sqlite.execute(
"CREATE TABLE IF NOT EXISTS uptime (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT, ok INTEGER, reason TEXT, status INTEGER, duration INTEGER, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);",
export async function uptimeCheck(url: string) {
let ok = true;
let reason: string;
pollRSSFeeds
@shreyasmakes
Poll RSS feeds This val periodically polls specified RSS feeds and send the author an email with new items. It checks each feed defined in rssFeeds for new content since the last run and sends an email with the details of the new items. Usage Fork @stevekrouse/rssFeeds and update it with your desired RSS feeds; Fork this val and replace the https://esm.town/v/stevekrouse/rssFeeds import with your newly forked val; Enjoy RSS updates on your email!
Cron
import { email } from "https://esm.town/v/std/email?v=9";
import { newRSSItems } from "https://esm.town/v/stevekrouse/newRSSItems";
export async function pollRSSFeeds({ lastRunAt }: Interval) {
return Promise.all(
Object.entries(rssFeeds).map(async ([name, url]) => {
valBlogWatcherCron
@charlypoly
An interactive, runnable TypeScript val by charlypoly
Cron
import { hasWebsiteChanged } from "https://esm.town/v/charlypoly/websiteHasChanged";
import { email } from "https://esm.town/v/std/email?v=12";
export default async function(interval: Interval) {
const hasChanged = await hasWebsiteChanged("https://blog.val.town", 0.6);
console.log("hasChanged", hasChanged);
streamingGif
@maxm
An animated gif that streams the current time:
HTTP
import { DateTime } from "https://esm.sh/luxon";
const scale = 0.15;
export default async function(req: Request) {
let timerId: number | undefined;
const body = new ReadableStream({
WebsiteFrontPageAlbum
@edgeeffect
To easily let browser JavaScript use albums from imgbb.com
you can call the URL of this Val with the parameter album holding the album ID string. https://edgeeffect-websitefrontpagealbum.web.val.run?album=XXXXXX It will reduce it down to a JSON object containing a list of the images in that album in the format: [
{"url": "...", "title": "..."},
...
] If there are any errors, it'll just return an empty JSON array.
HTTP
url as string,
title == undefined ? "NO TITLE :(" : title as string,
export default async function(req: Request): Promise<Response> {
const response: Array<[string, string]> = [];
const album = new URL(req.url).searchParams.get("album");
![vladimyr avatar](https://images.clerk.dev/oauth_github/img_2TYEBDWl6QE7Nsk4AHoXsDuwvBE.png)
bookmarkletHighlightForks
@vladimyr
Highlight forks on https://www.val.town/newest #bookmarklet
Script
function run() {
Array.from(document.querySelectorAll(":has(> div > [aria-label^=Forked])"))
.map(el => el.style.cssText = "border: 2px solid red;");
reactSVG2PNG
@jxnblk
@jsxImportSource npm:react
Script
url: URL;
const cache = new Map();
export function render(Component: React.ComponentType<SVGComponentProps>, headers: any = {}) {
return async function(req: Request): Promise<Response> {
const url = new URL(req.url);
const svg = renderToStaticMarkup(<Component request={req} url={url} />);
![petermillspaugh avatar](https://images.clerk.dev/oauth_github/img_2TFg9SMWVOcMJQ588IzCEa1VVhI.jpeg)
createSubscribers
@petermillspaugh
Val Town email subscriptions: create table Sibling Val to @petermillspaugh/emailSubscription — see docs there.
Script
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
export async function createSubscribers() {
await sqlite.execute(
CREATE TABLE IF NOT EXISTS subscribers (
![stevekrouse avatar](https://images.clerk.dev/uploaded/img_2PqHa2Gsy93xQrjh2w78Xu0cChW.jpeg)
beigeSawfish
@stevekrouse
Compress Response Isn't it cool that browsers can natively decompress stuff? There are a couple of supported compression algorithms: gzip (used below), compress , deflate , br . Learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding If you don't add the content-encoding header, the gzip result looks like: ���������
���.���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������YDA��"�� Which is incredibly short for a 500kb string! (Which shouldn't be surprising because it's just "hi" 250k times)
HTTP
import { gzip } from "npm:pako";
export default async function(req: Request): Promise<Response> {
return new Response(await gzip(JSON.stringify("hi".repeat(250_000))), {
headers: {
![jolodev avatar](https://images.clerk.dev/oauth_github/img_2SakdkztzBwl2LeDiM7ESTg5wFD.png)
camelCase
@jolodev
An interactive, runnable TypeScript val by jolodev
Script
export function camelCase(str) {
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
![envl avatar](https://images.clerk.dev/uploaded/img_2Sq6WoJbBMkhx5LiLFw2mwk2z9s.jpeg)
qq_svg
@envl
把 GitHub stars 数量转换为 QQ 等级 SVG 图片 Convert GitHub stars into SVG of QQ level QQ is a popular icq alternative from China 用法 Usage https://envl-qq_svg.web.val.run/:user/:repo?font_size=24&px=4&py=3 repo 选填 Optional px 选填 Optional py 选填 Optional font_size 选填 Optional 举例 Example: https://envl-qq_svg.web.val.run/envl
https://envl-qq_svg.web.val.run/freecodecamp/freecodecamp
HTTP
import { fetch } from "https://esm.town/v/std/fetch";
import { calc_qq_string } from "https://esm.town/v/envl/calc_qq_string";
export async function qq_svg(req: Request) {
const url = new URL(req.url);
const [user, repo] = url.pathname.substring(1).split("/");
![stevekrouse avatar](https://images.clerk.dev/uploaded/img_2PqHa2Gsy93xQrjh2w78Xu0cChW.jpeg)
versionLoggger
@stevekrouse
This helper function logs the version number of the val. import { logUrlVersion } from "https://esm.town/v/stevekrouse/versionLoggger";
logUrlVersion(import.meta.url); Or if you want a one liner with no imports: console.log("Version: " + import.meta.url.match(/[?&]v=([^&]*)/)?.at(1));
Script
This helper function logs the version number of the val.
```ts
export function logUrlVersion(url) {
const versionMatch = url.match(/[?&]v=([^&]*)/);
const version = versionMatch ? versionMatch[1] : "latest";
randomIntWithBounds
@samlloyd7
* Generates a random integer within a specified range
* @param max The upper bound of the random integer generation
* @param fromzero If true, generates from 0 to max-1. If false (default), generates from 1 to max.
* @returns A random integer in the specified range
HTTP
* @param fromzero If true, generates from 0 to max-1. If false (default), generates from 1 to max.
* @returns A random integer in the specified range
export default function randomInt(max: number, fromzero: boolean = false): number {
// Validate input
if (!Number.isInteger(max) || max <= 0) {
powderNotify
@chet
This doesnt work well
Script
const toInches = (cm: number) => Math.round(cm * 2.54 * 10) / 10;
/** This doesnt work well */
export async function powderNotify(location: string) {
const weather = await getWeather(location);
const snowDays = weather.forecast.forecastday.filter(day => {
![tr3ntg avatar](https://images.clerk.dev/oauth_github/img_2QyNgFrADFv8JHEaWwU4PAWF9zG.png)
md5
@tr3ntg
An interactive, runnable TypeScript val by tr3ntg
Script
export function md5(input) {
* These are the functions you'll usually want to call
function hex_md5(s) {
function b64_md5(s) {
function str_md5(s) {
function hex_hmac_md5(key, data) {
function b64_hmac_md5(key, data) {
function str_hmac_md5(key, data) {
function md5_vm_test() {
function core_md5(x, len) {