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
101
102
|
// import { Database } from "bun:sqlite";
// const db = new Database("data/bookmarks.db", { create: true });
import { TwitterBookmark } from "./twitter-api";
export interface CategorySuggestion {
categories: string[];
confidence: number;
reasoning: string;
}
export interface CategorizationRequest {
bookmark: TwitterBookmark;
userCategories: Array<{ name: string; criteria: string }>;
}
export interface CategorizationResponse {
suggestedCategories: CategorySuggestion[];
newCategories: string[];
summary: string;
keyTopics: string[];
}
export interface UserCategories {
categories: string[];
}
export const userCategories = [
{ name: "AI", criteria: "AI, machine learning, LLMs, all AI related stuff" },
{ name: "Nix", criteria: "Nix and NixOS" },
{ name: "Urbit", criteria: "anything related to Urbit" },
{
name: "Code",
criteria:
"other stuff related to software or software development (excludes the previous)",
},
{ name: "history", criteria: "posts about history or archeology" },
{ name: "Politics", criteria: "posts about politics" },
{
name: "China",
criteria:
"posts about China or in Chinese which don't fall under any other category",
},
{
name: "Japan",
criteria:
"posts about Japan or in Japanese which don't fall under any other category",
},
{ name: "Movies", criteria: "posts related to movies, anime or TV shows" },
{ name: "Demographics", criteria: "posts about demographics or birth rates" },
{ name: "Memes", criteria: "posts including memes or otherwise funny" },
];
type FileTree = Map<string, FileEntry[]>;
type FileEntry = { file: string } | { dir: FileTree };
// type FileTree = { dir: `${string}/`; files: string[]; dirs?: Record<string, FileTree> };
class Obsidian {
auth = `Bearer ${Bun.env.OBSIDIAN_API_KEY!}`;
url = `http://127.0.0.1:27123`;
tree: FileTree = new Map();
async getFiles() {}
async list() {
this.populate(this.tree, "/");
}
async populate(parent: FileTree, dir: `${string}/`) {
const entries: FileEntry[] = [];
const res = (await this.call("/vault/" + dir)) as { files: string[] };
for (let entry of res.files) {
if (entry.endsWith(".md")) entries.push({ file: entry });
else if (entry.endsWith("/")) this.populate(new Map(), entry as any);
// entries.push({ dir: new Map() });
}
parent.set(dir, entries);
}
async call(path: string) {
const headers = { Authorization: this.auth };
const opts = { headers };
const res = await fetch(this.url + path, opts);
const j = await res.json();
return j;
}
async post(path: string, body: string) {
const headers = { Authorization: this.auth };
const opts = { headers, method: "POST", body };
const res = await fetch(this.url + path, opts);
return res;
}
async saveToDisk(
bookmark: TwitterBookmark,
categorization: CategorizationResponse,
tags: string[],
) {
// append to file
const body = `\n\n`;
const opts = { method: "POST", headers, body };
const res = await this.post("/vault/", body);
if (!res.ok) {
const j = await res.json();
return { error: j.message };
} else return { ok: "" };
}
}
|