Trending Vals

31
jamiedubs avatar
jamiedubs
mediainfo
HTTP
Fetches details about media files (images, video, audio, etc) using Mediainfo.js . e.g. codecs used, duration, file sizes, ID3 tags, etc. Check out https://jamiedubs-mediainfo.web.val.run/ for a little interactive example Beta! API response not 100% stable, I might move things around still. Especially this summary field idea
1
32
andreterron avatar
andreterron
codeOnValTown
Script
Code on Val Town Adds a "Code on Val Town" ribbon to your page. This lets your website visitors navigate to the code behind it. This uses github-fork-ribbon-css under the hood. Usage Here are 2 different ways to add the "Code on Val Town" ribbon: 1. Wrap your fetch handler (recommended) import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50"; import { html } from "https://esm.town/v/stevekrouse/html?v=5"; export default modifyFetchHandler(async (req: Request): Promise<Response> => { return html(`<h2>Hello world!</h2>`); }); Example: @andreterron/openable_handler 2. Wrap your HTML string import { modifyHtmlString } from "https://esm.town/v/andreterron/codeOnValTown?v=50"; import { html } from "https://esm.town/v/stevekrouse/html?v=5"; export default async (req: Request): Promise<Response> => { return html(modifyHtmlString(`<h2>Hello world!</h2>`)); }; Example: @andreterron/openable_html Other ways We made sure this was very modular, so you can also add the ribbon using these methods: Get the element string directly: @andreterron/codeOnVT_ribbonElement Modify an HTTP Response: @andreterron/codeOnVT_modifyResponse Use .pipeThrough to append to a stream: @andreterron/InjectCodeOnValTownStream Customization Linking to the val These functions infer the val using the call stack or the request URL. If the inference isn't working, or if you want to ensure it links to a specific val, pass the val argument: modifyFetchHandler(handler, {val: { handle: "andre", name: "foo" }}) modifyHtmlString("<html>...", {val: { handle: "andre", name: "foo" }}) Styling You can set the style parameter to a css string to customize the ribbon. Check out github-fork-ribbon-css to learn more about how to style the element. modifyFetchHandler(handler, {style: ".github-fork-ribbon:before { background-color: #333; }"}) modifyHtmlString("<html>...", {style: ".github-fork-ribbon:before { background-color: #333; }"}) Here's how you can hide the ribbon on small screens: modifyFetchHandler(handler, {style: `@media (max-width: 768px) { .github-fork-ribbon { display: none !important; } }`}) To-dos [ ] Let users customize the ribbon. Some ideas are the text, color or placement.
5
33
yawnxyz avatar
yawnxyz
audioManager
Script
Usage: import { ai } from "https://esm.town/v/yawnxyz/ai"; import { AudioManager } from "https://esm.town/v/yawnxyz/audioManager"; let audio = new AudioManager(); let joke = await ai("tell me a joke in chinese!"); console.log('text', joke) let result = await audio.textToSpeechUpload(joke, {key: "random-joke.mp3"}); console.log('result:', result)
0
34
jxnblk avatar
jxnblk
gitHubSync
HTTP
GitHub Sync Sync vals from Val Town to GitHub and back What it does Using a fork of this val, you can specify a list of your own vals to export to a GitHub repo. Log in to the web app rendered by this val to manually click the Export button, which will read the contents of your vals and commit those vals as files to your GitHub repo. If you set up your repo to use the webhook in this val, any changes pushed to the GitHub repo will be synced back to the vals in your list. How to use Fork this val Create a new repo on GitHub for syncing your vals to (it can be public or private as long as you use the appropriate token scope) Add a webhook to your repo that points to your val's /webhook route, e.g. https://jxnblk-githubsync.web.val.run/webhook Create a Secret for your webhook In your Val Town Settings , add that same secret as GHSYNC_WEBHOOK_SECRET On GitHub, create a new GitHub OAuth app Use your val's web URL for the Homepage URL Set your val's /auth route as the Authorization callback URL , e.g. https://jxnblk-githubsync.web.val.run/auth Copy your new OAuth app's Client ID and add that as the GHSYNC_CLIENT_ID env var in Val Town Create and copy a Client Secret and add that as the GHSYNC_CLIENT_SECRET env var in Val Town On GitHub, create a new personal access token Ensure the token has read access to Contents for your repo Copy the access token and add that as the GHSYNC_GITHUB_TOKEN env var in Val Town In your val, edit the config in the val to point to your repo's owner and repo Edit the valNames list to include the name of the vals you'd like to sync to the repo In your val's Settings , ensure that under Permissions , Vals has Read & Write access Open your val's web URL in a new tab and click Log in with GitHub You should see a page listing your vals to be exported and the GitHub repo to sync to (if not, see below ) Click the Export button to sync your vals to the GitHub repo. This can take a moment and you'll need to wait for the browser request to complete. Once your vals are in the GitHub repo, edit them on GitHub or push changes to ensure that the webhook is working. You can check the Requests and Logs tabs for your val to debug any errors Demo This val is synced to GitHub: gitHubSyncDemoVal This is the GitHub repo: jxnblk/valtown-github-sync Troubleshooting If after logging in, you see a How to use GitHub Sync page, ensure that the config.owner in your val matches your GitHub username If the webhook doesn't work, ensure you have the webhook properly configured in the GitHub repo and that the GHSYNC_WEBHOOK_SECRET env var in Val Town matches the value in your GitHub repo. Additional Features The following list are ideas for how to improve this val. Feel free to implement some of these ideas. I would consider accepting pull requests for these features. [ ] Check timestamps on Val Town and GitHub to prevent syncing when an older version would overwrite a newer one [ ] Add an authenticated webhook to sync from Val Town to GitHub (e.g. a Cron val could trigger export to keep vals synced) [ ] A "No-UI" version of this will only webhooks (authentication would need to be considered)
6
35
stevekrouse avatar
stevekrouse
reloadOnSave
Script
Forked from stevekrouse/ReloadScript
6
36
stevekrouse avatar
stevekrouse
newUserWelcomeEmail
HTTP
Todos [ ] write a readme [ ] change this val's name, and update the clerk webhook
2
37
Shigigami avatar
Shigigami
chatApplication
HTTP
hi
1
38
gwoods22 avatar
gwoods22
scraper_template
Script
Website Scraper Template A basic website scraper template that can be ran on an interval to check for changes. It uses blob storage to save whatever data you'd like to compare the website to. Uses my sendNotification val to alert yourself of the change. Steps to use Add the scrapeURL you would like to scrape and pick a name for your blob storage blobKey . Change the "selector" value to fit your needs. cheerio is used to find the part of the page you'd like to check. Adjust the if statement to detect changes and update your blob Craft a message to be sent with sendNotification() function
1
39
stevekrouse avatar
stevekrouse
extractValInfo
Script
Forked from pomdtr/extractValInfo
0
40
stevekrouse avatar
stevekrouse
sqlite_admin
HTTP
Forked from stevekrouse/reactExample
3
41
stevekrouse avatar
stevekrouse
stripHTML
Script
Strip HTML Remove HTML tags from a string by parsing the HTML. There are certainly faster ways of doing this, ie html.replace(/<\/[^>]+(>|$)/g, "") // source https://stackoverflow.com/a/5002161 but this way is likely the most correct in that you're using a proper HTML parser.
1
42
stevekrouse avatar
stevekrouse
stockPrice
HTTP
Current Stock Price This val was designed to be used in Google Sheets, particularly because Google Finance stopped supporting Spotify inexplicably. Usage Copy and paste this into Google Sheets: =IMPORTDATA(CONCATENATE("https://stevekrouse-stockprice.web.val.run/?simple=1&symbol=","SPOT")) Replace "SPOT" with the symbol you want API This val currently uses Alpha Vantage's free plan (5 requests per minute & 500 per day). You can sign up for your own API Key here: https://www.alphavantage.co/support/#api-key
3
43
neverstew avatar
neverstew
queryParams
HTTP
Handling query params in requests Using the standard URL#searchParams method, you can grab query parameters out of any val that is operating using the Web API . This val demonstrates how to grab one or more query parameters. It returns the all the query parameters found as a json response.
2
44
maas avatar
maas
forwarder
Script
Unlimited Anonymous Emails All code can be triggered via email ( docs ), i.e. maas.forwarder@valtown.email . You can generate as many emails as you want to avoid giving away your personal email address and reduce spam. Getting started Log in to Val Town Fork this Val Publish the Val via 🔒 > Unlisted Copy the email endpoint via ⋮ > Endpoints > Copy email address Sign up for newsletters or services using this email ( maas.forwarder@valtown.email ). To remove their ability to email you, simply delete the val (or send it to /dev/null ).
3
45
msclicks avatar
msclicks
diabetesMealCalculatorApp
HTTP
Diabetics Guide Manageing Diabetes In 2025
1
46
stevekrouse avatar
stevekrouse
easyAQI
Script
easyAQI Get the Air Quality Index (AQI) for a location via open data sources. It's "easy" because it strings together multiple lower-level APIs to give you a simple interface for AQI. Accepts a location in basically any string format (ie "downtown manhattan") Uses Nominatim to turn that into longitude and latitude Finds the closest sensor to you on OpenAQ Pulls the readings from OpenAQ Calculates the AQI via EPA's NowCAST algorithm Uses EPA's ranking to classify the severity of the score (ie "Unhealthy for Sensitive Groups") It uses blob storage to cache the openai location id for your location string to skip a couple steps for the next time. Example usage @stevekrouse.easyAQI({ location: "brooklyn navy yard" }) // Returns { "aqi": 23.6, "severity": "Good" } Forkable example: val.town/v/stevekrouse.easyAQIExample Also useful for getting alerts when the AQI is unhealthy near you: https://www.val.town/v/stevekrouse.aqi
3
47
Johner avatar
Johner
cerebras_coder
HTTP
Forked from stevekrouse/cerebras_coder
0
48
maxm avatar
maxm
wide
HTTP
WIDE Store any unstructured JSON data. Retrieve it with an expressive and efficient query system. WIDE is a library and service hosted on Val Town. Authenticate and use it with your Val Town credentials, or fork it and connect it to your own Clickhouse Instance. import { ValSession } from 'https://esm.town/v/maxm/valSession'; import { Wide } from 'https://esm.town/v/maxm/wide'; // Use your Val Town API Token to create a session const wide = new Wide(await ValSession.new(Deno.env.get("valtown"))) // Write any data. await wide.write([ { user: {id: 1, name: 'Alice', email: 'alice@example.com' }}, { user: {id: 2, name: 'Bob', email: 'bob@example.com' }}, { user: {id: 3, name: 'Charlie', email: 'charlie@example.com' }}, ]); await wide.fields("user.") // => [ // { fieldName: "user.email", fieldType: "string", count: 3 }, // { fieldName: "user.id", fieldType: "number", count: 3 }, // { fieldName: "user.name", fieldType: "string", count: 3 } // ] await wide.values("user.email") // [ // { value: "bob@example.com", count: 1 }, // { value: "charlie@example.com", count: 1 }, // { value: "alice@example.com", count: 1 } // ] await wide.search({ start: new Date(Date.now() - 1000 * 60 * 10), end: new Date(), filters: [{ fieldName: "user.name", operator: "equals", value: "Alice" }], }) // [{ user: { name: "Alice", email: "alice@example.com", id: 1 } }];
4
49
mam avatar
mam
cerebras_coder
HTTP
Forked from stevekrouse/cerebras_coder
0
50
mikker avatar
mikker
dailySlackRoundup
Script
Sends a daily message to the team Slack channel. Similar to 37signals ' What did you work on today?
0
51
stevekrouse avatar
stevekrouse
passkeys_demo
HTTP
Passkeys Demo Passkeys are pretty neat! I wanted to get a demo working in Val Town so I ported over https://github.com/maximousblk/passkeys-demo. One challenge was that the original extensively uses DenoKV store with compound keys and values. I created @stevekrouse/DenoSyntheticKV as a replacement for DenoKV. It uses SuperJSON to encode the keys and values. You can find the client-side script for the main page here: @stevekrouse/passkey_script
6
52
schondaniel avatar
schondaniel
cerebras_coder
HTTP
Forked from stevekrouse/cerebras_coder
0
53
postpostscript avatar
postpostscript
lock
Script
Forked from stevekrouse/dlock
0
54
mattrw2 avatar
mattrw2
bookTheSperryChalet
Cron
A val that emails you when the Sperry Chalet in Glacier National Park has open vacancies
0
55
xkonti avatar
xkonti
ntfy
Script
Allows to publish a ntfy notification using a fluent builder configuration. Usage example import { ntfy } from "https://esm.town/v/xkonti/ntfy"; await ntfy() .toServer(Deno.env.get("ntfyServer")) .asUser(Deno.env.get("ntfyUser"), Deno.env.get("ntfyPassword")) .toTopic("testing") .withMessage("Hello there!") .withTitle("First test") .withViewAction("My website", "https://xkonti.tech") .withTags("package", "val-town") .withPriority("high") .publish(); ⚠️ For the notification to be sent it needs to be published ( publish function). Use helper Executes specified functions that can modify the notification. Can be used to streamline authentication, apply common operations, etc. import { ntfy } from "https://esm.town/v/xkonti/ntfy"; const toMyNtfyServer = (builder: ReturnType<typeof ntfy>) => { builder .toServer(Deno.env.get("ntfyServer")) .asUser(Deno.env.get("ntfyUser"), Deno.env.get("ntfyPassword")); }; await ntfy() .use(toMyNtfyServer) .toTopic('home-automation') .withMessage('You left the front door open') .publish(); You can pass it multiple functions. Functions toServer(url) - optional Specifies a server that the notification will be sent do. By default it's https://ntfy.sh . asUser(user, password) - optional Authenticates with the user and password. Please use ValTown's secrets for this. await ntfy() .asUser('user123', '12345') ... usingToken(token) - optional Authenticates using the provided token. Please use ValTown's secrets for this. await ntfy() .usingToken('some-token') ... toTopic(topic) - required Specifies which topic to publish the message to. await ntfy() .toTopic('home-automation') ... withMessage(message, markdown) - required Specifies the main message of the notification. You can also flag it as markdown by passing true as a second argument. By default markdown is false . await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') ... await ntfy() .toTopic('home-automation') .withMessage('Your garage is **flooding**!', true) ... withTitle(title) - optional Sets the title of the notification. await ntfy() .toTopic('home-automation') .withTitle('Garage') .withMessage('You left the front door open') ... withPriority(priority) - optional Sets the priority of the notification. Possible from lowest to highest priority: min , low , default , high , max await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withPriority('high') ... Alternatively you can use dedicated functions: .withMinPriority() , .withLowPriority() , .withDefaultPriority() , .withHighPriority() , .withMaxPriority() await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withHighPriority() ... withTags(...tags) - optional Sets tags of the notification. This overrides any previously existing tags. await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withTags('door', 'safety') ... withDelay(delay) - optional Sets the delay for notification delivery. Read ntfy docs for more info. await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withDelay('tomorrow, 10am') ... withViewAction(label, url, clear?) - optional Adds an action button that opens a website or app when tapped. label - Label of the action button in the notification url - URL to open when action is tapped clear - Clear notification after action button is tapped (defaults to false ) await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withViewAction('View Val', 'https://www.val.town/v/xkonti/ntfy') ... withBroadcastAction(label, intent?, extras?, clear?) - optional Adds an action button that sends an Android broadcast intent when tapped. label - Label of the action button in the notification intent - Android intent name, default is io.heckel.ntfy.USER_ACTION extras - Android intent extras. clear - Clear notification after action button is tapped (defaults to false ) await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withBroadcastAction('Selfie', 'Take picture', { 'cmd': 'pic' }) ... withHtmlAction(label, url, method?, headers?, body?, clear?) - optional Adds an action button that sends a HTTP request when tapped. label - Label of the action button in the notification url - URL to which the HTTP request will be sent method - HTTP method to use for request, default is POST headers - HTTP headers to pass in request. body - HTTP body as a string clear - Clear notification after action button is tapped (defaults to false ) await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withHtmlAction( 'Self-destruct', 'https://self.destruct/initiate', 'POST', { 'Authentication': 'Bearer 123' }, '{"countdown":60}' ) ... withClickUrl(url) - optional Makes the notification open the specified URL when clicked (tapped). withRawAttachment(filename, filedata) - optional Attached a file to the notification. Only one file can be attached. await ntfy() .toTopic('home-automation') .withMessage('You left the front door open') .withRawAttachment('todo.txt', 'Nothing!') ... withUrlAttachment(url) - optional Attaches a file that is hosted elsewhere (URL). withIcon(url) - optional Sets an icon for the notification. viaEmail(email) - optional Sends the notification via email instead. viaPhoneCall(number) - optional Sends the notification via a phone call . The number defaults to yes , which makes it use the first phone number defined on your ntfy account. withoutCache() - optional Disables the cache for the notification. Read the docs on caching for more info. withoutFirebase() - optional Disables Firebase forwarding for the notification. Read the docs on Firebase for more info. withUnifiedPush() - optional Indicates intent of using the Unified Push for the notification. Read the docs on Unified Push for more info.
6
56
mrshorts avatar
mrshorts
byob
Email
Forked from vawogbemi/byob
0
57
jonatan711 avatar
jonatan711
cerebras_coder
HTTP
Forked from stevekrouse/cerebras_coder
0
58
prashamtrivedi avatar
prashamtrivedi
summarizeHn
HTTP
A simple utility to summarize Hacker News articles, using Gemini Models. Inspired from Simon Willison's TIL , Prompt Inspiration
1
59
vawogbemi avatar
vawogbemi
byob
Email
BYOB - Build Your Own Bot You can chat with llms over email, the email thread functions as memory. The biggest thing is that you can instantly create a chat like interface with llms. Pair that with back end data and functions and you got something really powerful. Take it further Custom domains Use cloudflare email workers or a similiar service to create a custom email domain and route any incoming emails to this val. Use any email api set up with that domain to send emails ie. Sendgrid, Resend, Postmark. Toolings Llms can uses tools , meaning you can make this an agent and a whole lot more useful.
2
60
stevekrouse avatar
stevekrouse
dns_record_debugger
HTTP
Proxied Web Browser – DNS Debugger Proxied web browser for debugging new DNS records It's difficult to verify new DNS records. If you check too early, the old records may get cached on your computer, browser, or local network. This tool uses a proxied fetch so you can always view your web page uncached. Uses @std/fetch on the backend to ensure the DNS records of the request are from new places every request. Version 4 of this val also showed DNS records, pulled on the server, but I removed that featured, because those records are likely subject to caching. It'd be an improvement to this tool if we could add an uncached DNS check in here. In a couple min I wasn't able to find a free DNS checker API. Pull requests welcome!
3