0
0
Fork 0

Compare commits

...

4 Commits

6 changed files with 273 additions and 4 deletions

104
bin/gemini.ts Normal file
View File

@ -0,0 +1,104 @@
// Gemini v0.11.0 client
import {
BufReader,
readLines,
} from "https://deno.land/std@0.74.0/io/bufio.ts";
const content_buffer_size = 512;
const gemini_port = "1965";
interface Response {
status: string;
meta: string;
content?: Uint8Array;
}
async function fetchGemini(
url: URL,
redirect_count: number = 5,
): Promise<Response> {
if (url.protocol != "gemini:") {
throw new Error("Unsupported protocol");
}
const conn = await Deno.connectTls({
hostname: url.hostname,
port: parseInt(url.port != "" ? url.port : gemini_port),
});
try {
const request = new TextEncoder().encode(url.href + "\r\n");
if ((await conn.write(request)) != request.length) {
throw new Error("Write failure");
}
const reader = new BufReader(conn);
const header = await reader.readString("\n");
if (header == null) {
throw new Error("Unexpected end of stream");
}
const [_, status, meta] = header.match(/(\d+)\s+(.*)/) ?? ["", "", ""];
if (
status == undefined ||
status.length != 2 ||
(meta != undefined && meta.length > 1024)
) {
throw new Error("Received malformed response");
}
switch (status[0]) {
case "2":
break;
case "3":
if (redirect_count == 0) {
throw new Error("Too many redirects");
}
return fetchGemini(new URL(meta, url), redirect_count - 1);
default:
return {
status,
meta,
};
}
// Read remaining stream
let total = new Uint8Array(0);
let current = new Uint8Array(content_buffer_size);
for (
let count = await reader.read(current);
count != null;
count = await reader.read(current)
) {
let new_total = new Uint8Array(total.length + count);
new_total.set(total, 0);
new_total.set(current.slice(0, count), total.length);
total = new_total;
}
return {
status,
meta,
content: total,
};
} finally {
conn.close();
}
}
if (Deno.args.length != 1) {
console.error("Invalid number of arguments");
Deno.exit(1);
}
const res = await fetchGemini(new URL(Deno.args[0]));
console.log("Status " + res.status + " " + res.meta);
if (res.content != undefined) {
console.log(new TextDecoder().decode(res.content));
}

27
bin/jpath.ts Normal file
View File

@ -0,0 +1,27 @@
function traverse(obj: unknown, path: string = "") {
switch (typeof obj) {
case "object":
if (obj !== null) {
for (const [k, v] of Object.entries(obj)) {
traverse(v, Array.isArray(obj) ? `${path}[${k}]` : `${path}."${k}"`);
}
break;
}
default:
console.log(`${path}: ${obj}`);
}
}
if (import.meta.main) {
if (Deno.args.length !== 0) {
console.error("Usage: jpath");
Deno.exit(1);
}
const filename = Deno.args[0];
const json_text = new TextDecoder().decode(Deno.readAllSync(Deno.stdin));
const json = JSON.parse(json_text);
traverse(json);
}

27
bin/jsort.ts Normal file
View File

@ -0,0 +1,27 @@
function sortJSON<T>(obj: T): T {
if (typeof obj !== "object" || Array.isArray(obj) || obj === null) {
return obj;
}
return Object.fromEntries(
Object.entries(obj as Record<string, unknown>).sort((a, b) =>
a[0].localeCompare(b[0])
).map(([k, v]) => [k, sortJSON(v)]),
) as unknown as T;
}
if (import.meta.main) {
if (Deno.args.length !== 1) {
console.error("Usage: jsort <file>");
Deno.exit(1);
}
const filename = Deno.args[0];
const json_text = Deno.readTextFileSync(filename);
const json = JSON.parse(json_text);
const sorted = sortJSON(json);
const sorted_json_text = JSON.stringify(sorted);
Deno.writeTextFileSync(filename, sorted_json_text);
}

46
bin/watchfs.ts Normal file
View File

@ -0,0 +1,46 @@
async function* cleanWatch(dir: string) {
let lastPath = null;
let lastKind = null;
for await (
const { kind, paths } of Deno.watchFs(dir, {
recursive: true,
})
) {
for (const path of paths) {
if (lastPath != path || lastKind != kind) {
lastPath = path;
lastKind = kind;
yield { kind, path };
}
}
}
}
const color: Record<string, string> = {
"any": "color: grey",
"create": "color: lightgreen",
"modify": "color: yellow",
"remove": "color: red",
"access": "color: cyan",
};
const prefix: Record<string, string> = {
"any": "?",
"create": "+",
"modify": "~",
"remove": "-",
"access": ".",
};
let lastTime = new Date(0);
for await (
const event of cleanWatch(Deno.args[0])
) {
let time = new Date();
if (time.valueOf() - lastTime.valueOf() > 5000) console.log(time);
lastTime = time;
console.log(`%c${prefix[event.kind]} ${event.path}`, color[event.kind]);
}
export {};

60
bin/youtube.js Normal file
View File

@ -0,0 +1,60 @@
const search_url = "https://www.youtube.com/results?pbj=1&search_query=";
const headers = {
"X-YouTube-Client-Name": "1",
"X-YouTube-Client-Version": "2.20200319.09.00",
"Accept-Language": "en-US,en;q=0.5",
};
async function* mediaIterator(search) {
const result = await fetch(search_url + search, {
headers,
}).then((c) => c.json());
const videos = result?.[1]?.response?.contents?.twoColumnSearchResultsRenderer
?.primaryContents?.sectionListRenderer?.contents?.[0]?.itemSectionRenderer
?.contents;
if (videos == undefined) {
return;
}
for (const content of videos) {
if (content?.videoRenderer != undefined) {
const video = content.videoRenderer;
if (video.videoId == undefined) {
continue;
}
yield {
title: video.title?.accessibility?.accessibilityData?.label,
id: video.videoId,
};
} else if (content?.playlistRenderer != undefined) {
const playl = content.playlistRenderer;
if (playl.playlistId == undefined) {
continue;
}
yield {
title: playl.title?.simpleText,
id: playl.playlistId,
count: playl.videoCount,
};
}
}
}
if (Deno.args.length == 0) {
console.error("Expected search keywords");
Deno.exit(1);
}
for await (const media of mediaIterator(Deno.args.join("+"))) {
console.log(
media.title +
(media.count != undefined ? " <" + media.count + " Videos>" : "") +
":",
);
console.log("ytdl://" + media.id);
}

View File

@ -56,10 +56,13 @@ interface Card {
type Hand = Card[];
function cardString(card: Card, colored_wild = false): string {
function cardString(
card: Card,
opts: { colored_wild?: boolean; no_background?: boolean } = {},
): string {
return `${irc.bold()}${
irc.color(
!colored_wild && wild_cards.includes(card.value)
!opts.colored_wild && wild_cards.includes(card.value)
? irc.Color.Default
: card.color === "Blue"
? irc.Color.Blue
@ -68,7 +71,7 @@ function cardString(card: Card, colored_wild = false): string {
: card.color === "Red"
? irc.Color.Red
: irc.Color.Yellow,
irc.Color.Black,
opts.no_background ? undefined : irc.Color.Black,
)
}${card.value}${irc.reset()}`;
}
@ -167,7 +170,9 @@ class Game {
await msg.replyNotice(
`It's ${player}'s (${
this.#hands.get(player)!.length
}) turn. Current card: ${cardString(this.#current_card, true)}` +
}) turn. Current card: ${
cardString(this.#current_card, { colored_wild: true })
}` +
(this.#draw_count > 0 ? ` Draw count: ${this.#draw_count}` : ""),
);
}