Back to APIs list

Matrix API examples & templates

Use these vals as a playground to view and fork Matrix API examples and templates on Val Town. Run any example below or find templates that can be used as a pre-built solution.

playground

edit, run, and embed vals without requiring an account (or even js enabled!)

open

caveats:

  • logs don't stream
  • I haven't set up codemirror
  • only script vals supported

everything else should be fully functional.

you can prefill the editor with

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// (c) easrng 2024 all rights reserved
/** @jsx jsx */
/** @jsxFrag Fragment */
import {
createContext,
type FC,
Fragment,
jsx,
type PropsWithChildren,
useContext,
} from "https://deno.land/x/hono@v4.0.10/jsx/index.ts";
import { Hono } from "https://deno.land/x/hono@v4.0.10/mod.ts";
import { API_URL } from "https://esm.town/v/std/API_URL?v=5";
import { create } from "https://esm.town/v/websandbox/create";
import { parse as parseStack } from "npm:error-stack-parser-es@0.1.1";
import * as stylis from "npm:stylis@4.3.1";
import * as SuperJSON from "npm:superjson@2.2.1";
globalThis.addEventListener("unhandledrejection", (event) => {
event.preventDefault();
console.error("Uncaught (in promise) %o", event.reason)
});
const app = new Hono();
const Layout: FC<PropsWithChildren<{ title: string }>> = (props) => {
return (
<html>
<head>
<title>{props.title}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
{String.raw`
* {
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
color-scheme: light dark;
}
pre {
font-size: 1rem;
}
`}
</style>
</head>
<body>{props.children}</body>
</html>
);
};
const StacktraceLink: FC<{ href: string; line?: number; col?: number }> = ({
href,
line,
col,
}) => {
let link: string | JSX.Element = href;
try {
const url = new URL(href);
if (url.protocol === "https:" || url.protocol === "http:") {
link = <a href={url.href}>{url.href}</a>;
}
} catch {}
return (
<>
{link}
{line ? `:${line}${col ? ":" + col : ""}` : ""}
</>
);
};
const defaultFormat = (args: unknown[]) => {
const first = args[0];
let a = 0;
let out: string[] = [];
let styled: JSX.Element[] = [];
let css: string = "";
function flush() {
if (out.length) {
styled.push(css ? <span style={css}>{out}</span> : <>{out}</>);
out = [];
}
}
if (typeof first == "string" && args.length > 1) {
a++;
// Index of the first not-yet-appended character. Use this so we only
// have to append to `string` when a substitution occurs / at the end.
let appendedChars = 0;
for (let i = 0; i < first.length - 1; i++) {
if (first[i] == "%") {
const char = first[++i];
if (a < args.length) {
let formattedArg = "";
if (char == "s") {
// Format as a string.
formattedArg = String(args[a++]);
} else if (Array.prototype.includes.call(["d", "i"], char)) {
// Format as an integer.
const value = args[a++];
1
2
3
4
5
6
7
8
9
10
11
12
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=41";
import process from "node:process";
export const distance = async (req) => {
const searchParams = new URL(req.url).searchParams;
const destinationA = encodeURIComponent(String(searchParams.get("a")).trim());
const destinationB = encodeURIComponent(String(searchParams.get("b")).trim());
const obj = await fetchJSON(
`https://maps.googleapis.com/maps/api/distancematrix/json?destinations=${destinationA}&origins=${destinationB}&units=imperial&key=${process.env.GOOGLE_MAPS_API_KEY}`,
);
return Response.json(obj);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import * as denoDom from "https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm.ts";
const elementTypes = [
"HTMLTableColElement",
"HTMLFieldSetElement",
"SVGTSpanElement",
"HTMLDataListElement",
"SVGFEMergeElement",
"SVGFEGaussianBlurElement",
"SVGStyleElement",
"SVGViewElement",
"HTMLQuoteElement",
"HTMLBaseElement",
"HTMLFrameSetElement",
"SVGMPathElement",
"HTMLLIElement",
"HTMLHRElement",
"HTMLLegendElement",
"HTMLOptGroupElement",
"SVGFEDropShadowElement",
"SVGMarkerElement",
"HTMLDetailsElement",
"HTMLTimeElement",
"SVGTextContentElement",
"SVGSymbolElement",
"SVGMaskElement",
"SVGFEComponentTransferElement",
"HTMLFontElement",
"SVGScriptElement",
"HTMLObjectElement",
"SVGFEDiffuseLightingElement",
"SVGDefsElement",
"HTMLTableElement",
"SVGSwitchElement",
"SVGFEFuncRElement",
"SVGStopElement",
"HTMLFrameElement",
"SVGFETileElement",
"SVGImageElement",
"HTMLUListElement",
"SVGPolygonElement",
"HTMLParagraphElement",
"SVGTitleElement",
"SVGCircleElement",
"SVGPatternElement",
"HTMLMapElement",
"SVGClipPathElement",
"HTMLTableRowElement",
"SVGFESpecularLightingElement",
"SVGFECompositeElement",
"HTMLHeadingElement",
"SVGFEFuncGElement",
"SVGAnimationElement",
"SVGLinearGradientElement",
"SVGSetElement",
"SVGAnimateTransformElement",
"SVGFESpotLightElement",
"HTMLSlotElement",
"HTMLScriptElement",
"HTMLDirectoryElement",
"HTMLLabelElement",
"HTMLDialogElement",
"SVGFilterElement",
"SVGFEBlendElement",
"HTMLDListElement",
"HTMLStyleElement",
"HTMLParamElement",
"SVGFEMergeNodeElement",
"HTMLSourceElement",
"HTMLMenuElement",
"SVGTextElement",
"SVGAnimateMotionElement",
"SVGFEFuncBElement",
"HTMLBRElement",
"SVGComponentTransferFunctionElement",
"HTMLOutputElement",
"HTMLMarqueeElement",
"HTMLOptionElement",
"SVGEllipseElement",
"HTMLTitleElement",
"HTMLTableSectionElement",
"SVGFETurbulenceElement",
"SVGFEFloodElement",
"SVGFEDistantLightElement",
"SVGPolylineElement",
"HTMLProgressElement",
"HTMLTrackElement",
"HTMLOListElement",
"SVGGradientElement",
"SVGFEImageElement",
"HTMLTemplateElement",
"HTMLUnknownElement",
"HTMLMeterElement",
"HTMLModElement",
"SVGMetadataElement",
"SVGFEMorphologyElement",
"HTMLPictureElement",
"HTMLImageElement",
"SVGFEConvolveMatrixElement",
"SVGFEFuncAElement",
"HTMLEmbedElement",

Exact Cover sudoku Solver

Solves Sudoku puzzles via dancing-links.

Pass in a 9x9 Sudoku puzzle array (of arrays) with 0's for empty slots. Returns a solved puzzle or null if the puzzle can't be solved.

Example example_val

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Exact Cover Sudoku Solver
import { Sudoku } from "https://esm.town/v/saolsen/sudoku";
// An exact cover problem is solved by taking a list of possible constraint
// rows and finding a subset of them that covers each column exactly once.
// If thought of as a binary matrix, we would want to find a set of rows
// such that each column has exactly one 1 in it.
// To express sudoku as an exact cover problem, we need to encode all the
// possible numbers that can go into each slot, along with their
// constraints, in a way that obeys the rules of sudoku.
// There will be columns for each slot, row, column, and box,
// So there will be a row for each number and slot for each combination of
// constraints.
// eg [1,0...0, 1,0.....0,...] and
// [1,0...0, 0,1,0...0,...] are both solutions with 1 in the first slot,
// but the first also says that the 1 is in the first column and the second
// says that the 1 is in the second column. This way we can account for the
// constraints of the sudoku board and when we find a covering set of rows
// we know it is a valid solution to the sudoku board.
// The full matrix looks like this
// https://www.stolaf.edu/people/hansonr/sudoku/exactcovermatrix.htm
// Each column is a constraint, each row is a possible solution
// you pick a row to add to the solution, then cover up all the columns
// that have been satisfied, and all the rows that conflict with an already
// satisfied constraint. Then you have a smaller sub-matrix to continue
// picking rows from.
// Instead of actually using a matrix with so many columns, we can use a
// sparse representation, since most columns will be 0. We can make
// nodes for each of the 1s in the matrix and link them together in a
// linked list.
// By using a doubly linked list for the sparse matrix, we can easily remove
// and replace columns and rows from the matrix to make the search for a
// solution faster.
// The algorithm is called "Dancing Links" and it is actually pretty similar
// to the brute force backtracking algorithm. Instead of trying every
// possible number in every slot (and backtracking on failures), we select a
// slot value that we know doesn't conflict with any of the already resolved
// constraints, and then we remove all rows with constraints that are satisfied by
// the slot value so there are a lot fewer possibilities to try next and the search
// is much faster.
// There is some value for row,col
// This constraint makes sure the puzzle is fully filled out.
type SlotConstraint = {
kind: "slot";
row: number; // 0-8
col: number; // 0-8
};
// The value `value` is in row `row`.
// This constraint makes sure that each row has each number exactly once.
type RowConstraint = {
kind: "row";
row: number; // 0-8
value: number; // 1-9
};
// The value `value` is in col `col`.
// This constraint makes sure that each column has each number exactly once.
type ColConstraint = {
kind: "col";
col: number; // 0-8
value: number; // 1-9
};
// The value `value` is in box `box`.
// This constraint makes sure that each box has each number exactly once.
type BoxConstraint = {
kind: "box";
box: number; // 0-8
value: number; // 1-9
};
type Constraint = SlotConstraint | RowConstraint | ColConstraint | BoxConstraint;
const NUM_CONSTRAINTS = 324;
type Slot = {
row: number; // 0-8
col: number; // 0-8
value: number; // 1-9
};
function slotToConstraints(slot: Slot): Constraint[] {
console.assert(slot.row >= 0 && slot.row <= 8);
console.assert(slot.col >= 0 && slot.col <= 8);
console.assert(slot.value >= 1 && slot.value <= 9);
const { row, col, value } = slot;
const box = Math.floor(row / 3) * 3 + Math.floor(col / 3);
return [
{ kind: "slot", row, col },
{ kind: "row", row, value },
{ kind: "col", col, value },
{ kind: "box", box, value },
];
}

Usage

Create valimport {githubEmojiUrl} from "https://esm.town/v/karfau/githubEmoji"; console.log(githubEmojiUrl('+1')) //"https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png?v8"

or in a browser

<img src="https://karfau-githubEmoji.web.val.run/+1"></img>

Looks like in the preview.

curl https://karfau-githubEmoji.web.val.run/+1

(prints "https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png?v8")


If the name you pass (as argument or in the request path) is not in the list, it returns octocat

The list of names can be accessed using githubEmojiNames or by calling https://karfau-githubemoji.web.val.run/names

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { GITHUB_EMOJIS } from "https://esm.town/v/karfau/GITHUB_EMOJIS";
import { fetch } from "https://esm.town/v/std/fetch?v=4";
export const isEmoji = (key: string) => Object.hasOwn(GITHUB_EMOJIS, key);
export const githubEmojiUrl = (name: string, fallback = "octocat"): string =>
GITHUB_EMOJIS[isEmoji(name) ? name : fallback];
export const githubEmojiNames = (): string[] => Object.keys(GITHUB_EMOJIS);
export default async function githubEmoji(req: Request): Promise<Response> {
const contentType = req.headers.get("Content-Type");
const nameFromURL = new URL(req.url).pathname.split("/").filter(Boolean)[0];
if (nameFromURL === "names") {
return Response.json(githubEmojiNames());
}
const url = githubEmojiUrl(nameFromURL);
if (contentType || contentType === "application/json") {
return Response.json(url);
}
try {
return fetch(url);
} catch (error) {
console.error(req.url, nameFromURL, error);
return new Response(
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"64\" height=\"64\" viewBox=\"218.329 117.251 67.385 67.385\" xml:space=\"preserve\"><path style=\"stroke:#e9d535;stroke-width:0;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-li
{ headers: { "Content-Type": "image/svg" }, status: 307, statusText: "Currently not available" },
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { fetch } from "https://esm.town/v/std/fetch";
export const sendMatrixChatRoomMessage = async (
accessToken: string,
roomId: string,
transactionId: string,
message: MessageEventContent,
serverUrl: string = "https://matrix.org",
) => {
const url = `${serverUrl}/_matrix/client/v3/rooms/${
encodeURIComponent(roomId)
}/send/m.room.message/${encodeURIComponent(transactionId)}`;
//
const config = {
method: "PUT",
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(message),
};
return await fetch(url, config);
};
//
// Types
export interface MessageEventContent {
msgtype?: string;
body?: string;
format?: string;
formatted_body?: string;
info?: MessageEventContentInfo_;
url?: string;
membership?: RoomPhase;
"m.relates_to"?: {
event_id?: string;
is_falling_back?: boolean;
rel_type?: string;
"m.in_reply_to"?: {
[event_id: string]: string;
};
};
name?: string;
alias?: string;
join_rule?: string;
topic?: string;
display_name?: string;
displayname?: string;
avatar_url?: string;
is_direct?: boolean;
third_party_signed?: string;
last_active_ago?: number;
users?: {
[id: string]: number;
};
"m.new_content"?: MessageEventContent;
// todo
"org.matrix.msc1767.message"?: any[];
"org.matrix.msc1767.text"?: string;
// todo a big mess of fields that should be separated by event type
join_authorised_via_users_server?: string;
[other: string]: any;
}
export interface MessageEventContentInfo_ {
mimetype?: string;
size?: number;
h?: number;
w?: number;
thumbnail_url?: string;
thumbnail_info?: ThumbnailInfo_;
}
export interface ThumbnailInfo_ {
mimetype: string;
size: number;
h: number;
w: number;
}
export type RoomPhase = "join" | "invite" | "leave";
export interface LinkPreview_ {
url: string;
text?: string;
image_url?: string;
image_width?: number;
image_height?: number;
title?: string;
site_name?: string;
}
Runs every 15 min
Fork
1
2
3
4
import { watchReaderAndSendToMatrix } from "https://esm.town/v/vlad/watchReaderAndSendToMatrix";
export const watchReaderAndSendToMatrix_time2 = () =>
watchReaderAndSendToMatrix();
1
2
3
4
5
6
7
8
9
export function getRotationFromFFmpegDisplayMatrixString(str: string) {
let elements = str.match(/[-+]?\d+/g)?.map(Number) || [];
if (elements.length !== 9) {
// throw new Error("Exactly 9 matrix elements are required");
}
// Drop the last row
elements = elements.slice(-6);
return elements;
}
Runs every 15 min
Fork
1
2
3
4
import { watchReaderAndSendToMatrix } from "https://esm.town/v/vlad/watchReaderAndSendToMatrix";
export const watchReaderAndSendToMatrix_time3 = () =>
watchReaderAndSendToMatrix();
1
2
3
4
// https://stevekrouse-serverlessMatrixEchoBot.express.val.run/_matrix/push/v1/notify
export async function serverlessMatrixEchoBot(req, res) {
res.send(req.path);
}
Runs every 15 min
Fork
1
2
3
4
import { watchReaderAndSendToMatrix } from "https://esm.town/v/vlad/watchReaderAndSendToMatrix";
export const watchReaderAndSendToMatrix_time4 = () =>
watchReaderAndSendToMatrix();
Runs every 15 min
Fork
1
2
3
4
import { watchReaderAndSendToMatrix } from "https://esm.town/v/vlad/watchReaderAndSendToMatrix";
export const watchReaderAndSendToMatrix_time1 = () =>
watchReaderAndSendToMatrix();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { fetch } from "https://esm.town/v/std/fetch";
export const joinMatrixRoom = async (
accessToken: string,
roomId: string,
serverUrl: string = "https://matrix.org",
) => {
const url = `${serverUrl}/_matrix/client/v3/rooms/${
encodeURIComponent(roomId)
}/join`;
//
const config = {
method: "POST",
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
};
return await fetch(url, config);
};
1
2
3
4
export async function serverlessMatrixEchoBot2(req, res) {
console.log(req.path);
res.send("<h1>working!</h1>");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { sendMatrixChatRoomMessage } from "https://esm.town/v/vlad/sendMatrixChatRoomMessage";
export const sendMatrixChatRoomTextMessage = (
accessToken: string,
roomId: string,
message: string,
) => {
sendMatrixChatRoomMessage(
accessToken,
roomId,
new Date().toISOString(),
{ msgtype: "m.text", body: message },
);
};