Merge pull request #2 from indexxing/feature/improve-interaction-logging
This commit is contained in:
commit
76b07f76cd
12 changed files with 498 additions and 26 deletions
|
|
@ -1,7 +1,9 @@
|
||||||
# Comma-separated list of users who can use the bot (delete var if you want everyone to be able to use it)
|
# Comma-separated list of users who can use the bot (delete var if you want everyone to be able to use it)
|
||||||
AUTHORIZED_USERS=""
|
AUTHORIZED_USERS=""
|
||||||
|
|
||||||
SERVICE="https://pds.indexx.dev" # PDS service URL (optional)
|
# PDS service URL (optional)
|
||||||
|
SERVICE="https://pds.indexx.dev"
|
||||||
|
|
||||||
DB_PATH="data/sqlite.db"
|
DB_PATH="data/sqlite.db"
|
||||||
GEMINI_MODEL="gemini-2.0-flash-lite"
|
GEMINI_MODEL="gemini-2.0-flash-lite"
|
||||||
|
|
||||||
|
|
|
||||||
14
bun.lock
14
bun.lock
|
|
@ -5,20 +5,20 @@
|
||||||
"name": "bsky-echo",
|
"name": "bsky-echo",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atproto/syntax": "^0.4.0",
|
"@atproto/syntax": "^0.4.0",
|
||||||
"@google/genai": "^1.10.0",
|
"@google/genai": "^1.11.0",
|
||||||
"@skyware/bot": "^0.3.12",
|
"@skyware/bot": "^0.3.12",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"consola": "^3.4.2",
|
"consola": "^3.4.2",
|
||||||
"drizzle-orm": "^0.44.3",
|
"drizzle-orm": "^0.44.4",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"zod": "^4.0.5",
|
"zod": "^4.0.14",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.2.19",
|
"@types/bun": "^1.2.19",
|
||||||
"drizzle-kit": "^0.31.4",
|
"drizzle-kit": "^0.31.4",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5.8.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -107,7 +107,7 @@
|
||||||
|
|
||||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="],
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="],
|
||||||
|
|
||||||
"@google/genai": ["@google/genai@1.10.0", "", { "dependencies": { "google-auth-library": "^9.14.2", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.11.0" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-PR4tLuiIFMrpAiiCko2Z16ydikFsPF1c5TBfI64hlZcv3xBEApSCceLuDYu1pNMq2SkNh4r66J4AG+ZexBnMLw=="],
|
"@google/genai": ["@google/genai@1.11.0", "", { "dependencies": { "google-auth-library": "^9.14.2", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.11.0" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-4XFAHCvU91ewdWOU3RUdSeXpDuZRJHNYLqT9LKw7WqPjRQcEJvVU+VOU49ocruaSp8VuLKMecl0iadlQK+Zgfw=="],
|
||||||
|
|
||||||
"@skyware/bot": ["@skyware/bot@0.3.12", "", { "dependencies": { "@atcute/bluesky": "^1.0.7", "@atcute/bluesky-richtext-builder": "^1.0.1", "@atcute/client": "^2.0.3", "@atcute/ozone": "^1.0.5", "quick-lru": "^7.0.0", "rate-limit-threshold": "^0.1.5" }, "optionalDependencies": { "@skyware/firehose": "^0.3.2", "@skyware/jetstream": "^0.2.2" } }, "sha512-5OqTtwItYsBFMh0nwrxfsqgXrvRaJzg1P+ghMV4rlRGwHhdRgBJcnYQYgUqqREFcB247yGo73LNyqq7kHEwV7Q=="],
|
"@skyware/bot": ["@skyware/bot@0.3.12", "", { "dependencies": { "@atcute/bluesky": "^1.0.7", "@atcute/bluesky-richtext-builder": "^1.0.1", "@atcute/client": "^2.0.3", "@atcute/ozone": "^1.0.5", "quick-lru": "^7.0.0", "rate-limit-threshold": "^0.1.5" }, "optionalDependencies": { "@skyware/firehose": "^0.3.2", "@skyware/jetstream": "^0.2.2" } }, "sha512-5OqTtwItYsBFMh0nwrxfsqgXrvRaJzg1P+ghMV4rlRGwHhdRgBJcnYQYgUqqREFcB247yGo73LNyqq7kHEwV7Q=="],
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@
|
||||||
|
|
||||||
"drizzle-kit": ["drizzle-kit@0.31.4", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="],
|
"drizzle-kit": ["drizzle-kit@0.31.4", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="],
|
||||||
|
|
||||||
"drizzle-orm": ["drizzle-orm@0.44.3", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-8nIiYQxOpgUicEL04YFojJmvC4DNO4KoyXsEIqN44+g6gNBr6hmVpWk3uyAt4CaTiRGDwoU+alfqNNeonLAFOQ=="],
|
"drizzle-orm": ["drizzle-orm@0.44.4", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-ZyzKFpTC/Ut3fIqc2c0dPZ6nhchQXriTsqTNs4ayRgl6sZcFlMs9QZKPSHXK4bdOf41GHGWf+FrpcDDYwW+W6Q=="],
|
||||||
|
|
||||||
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
||||||
|
|
||||||
|
|
@ -217,7 +217,7 @@
|
||||||
|
|
||||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
"zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="],
|
"zod": ["zod@4.0.14", "", {}, "sha512-nGFJTnJN6cM2v9kXL+SOBq3AtjQby3Mv5ySGFof5UGRHrRioSJ5iG680cYNjE/yWk671nROcpPj4hAS8nyLhSw=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||||
|
|
||||||
|
|
|
||||||
3
drizzle/0003_flowery_korvac.sql
Normal file
3
drizzle/0003_flowery_korvac.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE `interactions` ADD `post` text;--> statement-breakpoint
|
||||||
|
ALTER TABLE `interactions` ADD `response` text;--> statement-breakpoint
|
||||||
|
ALTER TABLE `interactions` ADD `muted` integer;
|
||||||
255
drizzle/meta/0003_snapshot.json
Normal file
255
drizzle/meta/0003_snapshot.json
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "30d38111-8e11-4d7d-99e8-cbafd962ca62",
|
||||||
|
"prevId": "11e8b31f-8e38-4013-8d50-bec6177b015a",
|
||||||
|
"tables": {
|
||||||
|
"interactions": {
|
||||||
|
"name": "interactions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"name": "uri",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"did": {
|
||||||
|
"name": "did",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"name": "post",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"name": "response",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"muted": {
|
||||||
|
"name": "muted",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "CURRENT_TIMESTAMP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"interactions_uri_unique": {
|
||||||
|
"name": "interactions_uri_unique",
|
||||||
|
"columns": [
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"memory_block_entries": {
|
||||||
|
"name": "memory_block_entries",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"block_id": {
|
||||||
|
"name": "block_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"name": "label",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"added_by": {
|
||||||
|
"name": "added_by",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "CURRENT_TIMESTAMP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"memory_block_entries_block_id_memory_blocks_id_fk": {
|
||||||
|
"name": "memory_block_entries_block_id_memory_blocks_id_fk",
|
||||||
|
"tableFrom": "memory_block_entries",
|
||||||
|
"tableTo": "memory_blocks",
|
||||||
|
"columnsFrom": [
|
||||||
|
"block_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"memory_blocks": {
|
||||||
|
"name": "memory_blocks",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"did": {
|
||||||
|
"name": "did",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'memory'"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'User memory'"
|
||||||
|
},
|
||||||
|
"mutable": {
|
||||||
|
"name": "mutable",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"muted_threads": {
|
||||||
|
"name": "muted_threads",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"uri": {
|
||||||
|
"name": "uri",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"rkey": {
|
||||||
|
"name": "rkey",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"muted_at": {
|
||||||
|
"name": "muted_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "CURRENT_TIMESTAMP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"muted_threads_uri_unique": {
|
||||||
|
"name": "muted_threads_uri_unique",
|
||||||
|
"columns": [
|
||||||
|
"uri"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
},
|
||||||
|
"muted_threads_rkey_unique": {
|
||||||
|
"name": "muted_threads_rkey_unique",
|
||||||
|
"columns": [
|
||||||
|
"rkey"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,13 @@
|
||||||
"when": 1753682242260,
|
"when": 1753682242260,
|
||||||
"tag": "0002_green_millenium_guard",
|
"tag": "0002_green_millenium_guard",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 3,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1754166687687,
|
||||||
|
"tag": "0003_flowery_korvac",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -14,17 +14,17 @@
|
||||||
"drizzle-kit": "^0.31.4"
|
"drizzle-kit": "^0.31.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atproto/syntax": "^0.4.0",
|
"@atproto/syntax": "^0.4.0",
|
||||||
"@google/genai": "^1.10.0",
|
"@google/genai": "^1.11.0",
|
||||||
"@skyware/bot": "^0.3.12",
|
"@skyware/bot": "^0.3.12",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"consola": "^3.4.2",
|
"consola": "^3.4.2",
|
||||||
"drizzle-orm": "^0.44.3",
|
"drizzle-orm": "^0.44.4",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"zod": "^4.0.5"
|
"zod": "^4.0.14"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/indexxing/echo"
|
"url": "https://github.com/indexxing/echo"
|
||||||
|
|
|
||||||
BIN
sqlite.db
Normal file
BIN
sqlite.db
Normal file
Binary file not shown.
|
|
@ -1,6 +1,7 @@
|
||||||
import { drizzle } from "drizzle-orm/bun-sqlite";
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
||||||
import { Database } from "bun:sqlite";
|
import { Database } from "bun:sqlite";
|
||||||
|
import * as schema from "./schema";
|
||||||
import { env } from "../env";
|
import { env } from "../env";
|
||||||
|
|
||||||
const sqlite = new Database(env.DB_PATH);
|
const sqlite = new Database(env.DB_PATH);
|
||||||
export default drizzle(sqlite);
|
export default drizzle(sqlite, { schema });
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ export const interactions = sqliteTable("interactions", {
|
||||||
id: integer().primaryKey({ autoIncrement: true }),
|
id: integer().primaryKey({ autoIncrement: true }),
|
||||||
uri: text().unique(),
|
uri: text().unique(),
|
||||||
did: text(),
|
did: text(),
|
||||||
|
post: text(),
|
||||||
|
response: text(),
|
||||||
|
muted: integer({ mode: "boolean" }),
|
||||||
created_at: integer({ mode: "timestamp" }).default(sql`CURRENT_TIMESTAMP`),
|
created_at: integer({ mode: "timestamp" }).default(sql`CURRENT_TIMESTAMP`),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
import { isAuthorizedUser, logInteraction } from "../utils/interactions";
|
import {
|
||||||
|
isAuthorizedUser,
|
||||||
|
logInteraction,
|
||||||
|
getRecentInteractions,
|
||||||
|
} from "../utils/interactions";
|
||||||
import * as threadUtils from "../utils/thread";
|
import * as threadUtils from "../utils/thread";
|
||||||
import modelPrompt from "../model/prompt.txt";
|
import modelPrompt from "../model/prompt.txt";
|
||||||
import { GoogleGenAI } from "@google/genai";
|
import { GoogleGenAI } from "@google/genai";
|
||||||
import { interactions } from "../db/schema";
|
|
||||||
import { type Post } from "@skyware/bot";
|
import { type Post } from "@skyware/bot";
|
||||||
import * as c from "../constants";
|
import * as c from "../constants";
|
||||||
import * as tools from "../tools";
|
import * as tools from "../tools";
|
||||||
import consola from "consola";
|
import consola from "consola";
|
||||||
import { env } from "../env";
|
import { env } from "../env";
|
||||||
|
import { MemoryHandler } from "../utils/memory";
|
||||||
|
import * as yaml from "js-yaml";
|
||||||
|
|
||||||
const logger = consola.withTag("Post Handler");
|
const logger = consola.withTag("Post Handler");
|
||||||
|
|
||||||
type SupportedFunctionCall = typeof c.SUPPORTED_FUNCTION_CALLS[number];
|
type SupportedFunctionCall = typeof c.SUPPORTED_FUNCTION_CALLS[number];
|
||||||
|
|
||||||
async function generateAIResponse(parsedThread: string) {
|
async function generateAIResponse(memory: string, parsedThread: string) {
|
||||||
const genai = new GoogleGenAI({
|
const genai = new GoogleGenAI({
|
||||||
apiKey: env.GEMINI_API_KEY,
|
apiKey: env.GEMINI_API_KEY,
|
||||||
});
|
});
|
||||||
|
|
@ -39,6 +44,9 @@ async function generateAIResponse(parsedThread: string) {
|
||||||
.replace("{{ administrator }}", env.ADMIN_HANDLE)
|
.replace("{{ administrator }}", env.ADMIN_HANDLE)
|
||||||
.replace("{{ handle }}", env.HANDLE),
|
.replace("{{ handle }}", env.HANDLE),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: memory,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -126,10 +134,13 @@ export async function handler(post: Post): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await logInteraction(post);
|
const isMuted = await threadUtils.isThreadMuted(post);
|
||||||
|
if (isMuted) {
|
||||||
if (await threadUtils.isThreadMuted(post)) {
|
|
||||||
logger.warn("Thread is muted.");
|
logger.warn("Thread is muted.");
|
||||||
|
await logInteraction(post, {
|
||||||
|
responseText: null,
|
||||||
|
wasMuted: true,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,13 +148,42 @@ export async function handler(post: Post): Promise<void> {
|
||||||
const parsedThread = threadUtils.parseThread(thread);
|
const parsedThread = threadUtils.parseThread(thread);
|
||||||
logger.success("Generated thread context:", parsedThread);
|
logger.success("Generated thread context:", parsedThread);
|
||||||
|
|
||||||
const inference = await generateAIResponse(parsedThread);
|
const botMemory = new MemoryHandler(
|
||||||
|
env.DID,
|
||||||
|
await MemoryHandler.getBlocks(env.DID),
|
||||||
|
);
|
||||||
|
const userMemory = new MemoryHandler(
|
||||||
|
post.author.did,
|
||||||
|
await MemoryHandler.getBlocks(post.author.did),
|
||||||
|
);
|
||||||
|
|
||||||
|
const recentInteractions = await getRecentInteractions(
|
||||||
|
post.author.did,
|
||||||
|
thread,
|
||||||
|
);
|
||||||
|
|
||||||
|
const memory = yaml.dump({
|
||||||
|
users_with_memory_blocks: {
|
||||||
|
[env.HANDLE]: botMemory.parseBlocks(),
|
||||||
|
[post.author.handle]: userMemory.parseBlocks(),
|
||||||
|
},
|
||||||
|
recent_interactions: recentInteractions,
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log("Parsed memory blocks: ", memory);
|
||||||
|
|
||||||
|
const inference = await generateAIResponse(memory, parsedThread);
|
||||||
logger.success("Generated text:", inference.text);
|
logger.success("Generated text:", inference.text);
|
||||||
|
|
||||||
const responseText = inference.text;
|
const responseText = inference.text;
|
||||||
if (responseText) {
|
if (responseText) {
|
||||||
await sendResponse(post, responseText);
|
await sendResponse(post, responseText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await logInteraction(post, {
|
||||||
|
responseText: responseText ?? null,
|
||||||
|
wasMuted: false,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Error in post handler:", error);
|
logger.error("Error in post handler:", error);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { interactions } from "../db/schema";
|
import { interactions } from "../db/schema";
|
||||||
import type { Post } from "@skyware/bot";
|
import type { Post } from "@skyware/bot";
|
||||||
|
import { desc, notInArray } from "drizzle-orm";
|
||||||
import { env } from "../env";
|
import { env } from "../env";
|
||||||
import db from "../db";
|
import db from "../db";
|
||||||
|
|
||||||
|
|
@ -9,11 +10,41 @@ export function isAuthorizedUser(did: string) {
|
||||||
: env.AUTHORIZED_USERS.includes(did as any);
|
: env.AUTHORIZED_USERS.includes(did as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logInteraction(post: Post): Promise<void> {
|
export async function logInteraction(
|
||||||
await db.insert(interactions).values([{
|
post: Post,
|
||||||
uri: post.uri,
|
options: {
|
||||||
did: post.author.did,
|
responseText: string | null;
|
||||||
}]);
|
wasMuted: boolean;
|
||||||
|
},
|
||||||
|
): Promise<void> {
|
||||||
|
await db.insert(interactions).values([
|
||||||
|
{
|
||||||
|
uri: post.uri,
|
||||||
|
did: post.author.did,
|
||||||
|
post: post.text,
|
||||||
|
response: options.responseText,
|
||||||
|
muted: options.wasMuted,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
console.log(`Logged interaction, initiated by @${post.author.handle}`);
|
console.log(`Logged interaction, initiated by @${post.author.handle}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRecentInteractions(did: string, thread: Post[]) {
|
||||||
|
const threadUris = thread.map((p) => p.uri);
|
||||||
|
|
||||||
|
const recentInteractions = await db.query.interactions.findMany({
|
||||||
|
where: (interactions, { eq, and, notInArray }) =>
|
||||||
|
and(
|
||||||
|
eq(interactions.did, did),
|
||||||
|
notInArray(interactions.uri, threadUris),
|
||||||
|
),
|
||||||
|
orderBy: (interactions, { desc }) => [desc(interactions.created_at)],
|
||||||
|
limit: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
return recentInteractions.map((i) => ({
|
||||||
|
post: i.post,
|
||||||
|
response: i.response,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
130
src/utils/memory.ts
Normal file
130
src/utils/memory.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
import { and, desc, eq } from "drizzle-orm";
|
||||||
|
import db from "../db";
|
||||||
|
import { memory_block_entries, memory_blocks } from "../db/schema";
|
||||||
|
import * as yaml from "js-yaml";
|
||||||
|
|
||||||
|
type MemoryBlock = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
mutable: boolean;
|
||||||
|
entries: Entry[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
id: number;
|
||||||
|
block_id: number;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
added_by: string | null;
|
||||||
|
created_at: Date | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class MemoryHandler {
|
||||||
|
did: string;
|
||||||
|
blocks: MemoryBlockHandler[];
|
||||||
|
|
||||||
|
constructor(did: string, blocks: MemoryBlockHandler[]) {
|
||||||
|
this.did = did;
|
||||||
|
this.blocks = blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getBlocks(did: string) {
|
||||||
|
const blocks = await db
|
||||||
|
.select({
|
||||||
|
id: memory_blocks.id,
|
||||||
|
name: memory_blocks.name,
|
||||||
|
description: memory_blocks.description,
|
||||||
|
mutable: memory_blocks.mutable,
|
||||||
|
})
|
||||||
|
.from(memory_blocks)
|
||||||
|
.where(eq(memory_blocks.did, did));
|
||||||
|
|
||||||
|
const hydratedBlocks = [];
|
||||||
|
|
||||||
|
for (const block of blocks) {
|
||||||
|
const entries = await db
|
||||||
|
.select()
|
||||||
|
.from(memory_block_entries)
|
||||||
|
.where(eq(memory_block_entries.block_id, block.id))
|
||||||
|
.orderBy(desc(memory_block_entries.id))
|
||||||
|
.limit(15);
|
||||||
|
|
||||||
|
hydratedBlocks.push({
|
||||||
|
...block,
|
||||||
|
entries,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hydratedBlocks.length == 0) {
|
||||||
|
const [newBlock] = await db
|
||||||
|
.insert(memory_blocks)
|
||||||
|
.values([
|
||||||
|
{
|
||||||
|
did,
|
||||||
|
name: "memory",
|
||||||
|
description: "User memory",
|
||||||
|
mutable: false,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
hydratedBlocks.push({
|
||||||
|
...newBlock,
|
||||||
|
entries: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return hydratedBlocks.map(
|
||||||
|
(block) =>
|
||||||
|
new MemoryBlockHandler(
|
||||||
|
block as MemoryBlock,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseBlocks() {
|
||||||
|
return this.blocks.map((handler) => ({
|
||||||
|
name: handler.block.name,
|
||||||
|
description: handler.block.description,
|
||||||
|
entries: handler.block.entries.map((entry) => ({
|
||||||
|
label: entry.label,
|
||||||
|
value: entry.value,
|
||||||
|
added_by: entry.added_by || "nobody",
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MemoryBlockHandler {
|
||||||
|
block: MemoryBlock;
|
||||||
|
|
||||||
|
constructor(block: MemoryBlock) {
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createEntry(label: string, value: string) {
|
||||||
|
const [entry] = await db
|
||||||
|
.insert(memory_block_entries)
|
||||||
|
.values([
|
||||||
|
{
|
||||||
|
block_id: this.block.id,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
return {
|
||||||
|
added_to_memory: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.block.entries.push(entry);
|
||||||
|
|
||||||
|
return {
|
||||||
|
added_to_memory: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue