Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/bot-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Bot Runner

Minimal Matrix event listener that will enqueue bot jobs in the future.

## Environment

- `MATRIX_URL` (default: `http://localhost:8008`)
- `BOT_RUNNER_USERNAME` (default: `bot-runner`)
- `BOT_RUNNER_PASSWORD` (default: `password`)
- `LOG_LEVELS` (optional, default: `*=info`)

## Running

- `pnpm start`
- `pnpm start:development`
93 changes: 93 additions & 0 deletions packages/bot-runner/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import './setup-logger'; // This should be first
import { RoomMemberEvent, RoomEvent, createClient } from 'matrix-js-sdk';
import { PgAdapter, PgQueuePublisher } from '@cardstack/postgres';
import { logger } from '@cardstack/runtime-common';

const log = logger('bot-runner');
const startTime = Date.now();

const matrixUrl = process.env.MATRIX_URL || 'http://localhost:8008';
const botUsername = process.env.BOT_RUNNER_USERNAME || 'bot-runner';
const botPassword = process.env.BOT_RUNNER_PASSWORD || 'password';

(async () => {
let client = createClient({
baseUrl: matrixUrl,
});

let auth = await client.loginWithPassword(botUsername, botPassword).catch(
(error) => {
log.error(error);
log.error(
`Bot runner could not login to Matrix at ${matrixUrl}. Check credentials and server availability.`,
);
process.exit(1);
},
);

log.info(`logged in as ${auth.user_id}`);

let dbAdapter = new PgAdapter();
let queuePublisher = new PgQueuePublisher(dbAdapter);

const shutdown = async () => {
log.info('shutting down bot runner...');
try {
await queuePublisher.destroy();
await dbAdapter.close();
} catch (error) {
log.error('error during shutdown', error);
process.exit(1);
}
process.exit(0);
};

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

client.on(RoomMemberEvent.Membership, function (event, member) {
if (event.event.origin_server_ts! < startTime) {
return;
}
if (member.membership === 'invite' && member.userId === auth.user_id) {
client
.joinRoom(member.roomId)
.then(function () {
log.info('%s auto-joined %s', member.name, member.roomId);
})
.catch(function (err) {
log.info(
'Error joining room after invite (user may have left before join)',
err,
);
});
}
});

client.on(RoomEvent.Timeline, async (event, room, toStartOfTimeline) => {
if (!room || toStartOfTimeline) {
return;
}

let senderMatrixUserId = event.getSender();
if (!senderMatrixUserId || senderMatrixUserId === auth.user_id) {
return;
}

let eventBody = event.getContent()?.body || '';
log.info(
'received event in room %s (%s): %s',
room.name,
room.roomId,
eventBody,
);

// TODO: enqueue bot command job via queuePublisher.publish(...)
});

client.startClient();
log.info('bot runner listening for Matrix events');
})().catch((error) => {
log.error('bot runner failed to start', error);
process.exit(1);
});
21 changes: 21 additions & 0 deletions packages/bot-runner/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@cardstack/bot-runner",
"dependencies": {
"@cardstack/postgres": "workspace:*",
"@cardstack/runtime-common": "workspace:*",
"matrix-js-sdk": "catalog:",
"ts-node": "^10.9.2",
"typescript": "catalog:"
},
"devDependencies": {
"@cardstack/local-types": "workspace:*",
"@types/node": "catalog:"
},
"scripts": {
"start": "NODE_NO_WARNINGS=1 ts-node --transpileOnly main",
"start:development": "NODE_NO_WARNINGS=1 PGDATABASE=boxel PGPORT=5435 ts-node --transpileOnly main"
},
"volta": {
"extends": "../../package.json"
}
}
5 changes: 5 additions & 0 deletions packages/bot-runner/setup-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { makeLogDefinitions } from '@cardstack/runtime-common';

(globalThis as any)._logDefinitions = makeLogDefinitions(
process.env.LOG_LEVELS || '*=info',
);
18 changes: 18 additions & 0 deletions packages/bot-runner/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es2023",
"lib": ["es2023", "dom"],
"module": "nodenext",
"moduleResolution": "nodenext",
"allowSyntheticDefaultImports": true,
"noEmit": true,
"inlineSourceMap": true,
"inlineSources": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"strict": true,
"types": ["@cardstack/local-types"]
},
"include": ["./**/*"]
}
4 changes: 3 additions & 1 deletion packages/host/app/commands/create-ai-assistant-room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default class CreateAiAssistantRoomCommand extends HostBaseCommand<
let { matrixService } = this;
let userId = matrixService.userId;
let aiBotFullId = matrixService.aiBotUserId;
let botRunnerFullId = matrixService.botRunnerUserId;

if (!userId) {
throw new Error(
Expand Down Expand Up @@ -88,7 +89,8 @@ export default class CreateAiAssistantRoomCommand extends HostBaseCommand<
const [roomResult, commandModule] = await Promise.all([
await matrixService.createRoom({
preset: matrixService.privateChatPreset,
invite: [aiBotFullId],
//TODO: botRunner is here for testing purposes. Remove once finished
invite: [aiBotFullId, botRunnerFullId],
name: input.name,
room_alias_name: encodeURIComponent(
`${input.name} - ${format(
Expand Down
6 changes: 6 additions & 0 deletions packages/host/app/services/matrix-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {
} from '@cardstack/runtime-common';
import {
aiBotUsername,
botRunnerUsername,
logger,
isCardInstance,
Deferred,
Expand Down Expand Up @@ -400,6 +401,11 @@ export default class MatrixService extends Service {
return `@${aiBotUsername}:${server}`;
}

get botRunnerUserId() {
let server = this.userId!.split(':')[1];
return `@${botRunnerUsername}:${server}`;
}

get userName() {
return this.userId ? getMatrixUsername(this.userId) : null;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/matrix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@
"start:admin": "ts-node --transpileOnly ./scripts/admin-console",
"stop:admin": "docker stop synapse-admin && docker rm synapse-admin",
"register-bot-user": "MATRIX_USERNAME=aibot MATRIX_PASSWORD=pass ts-node --transpileOnly ./scripts/register-test-user.ts",
"register-bot-runner": "MATRIX_IS_ADMIN=TRUE MATRIX_USERNAME=bot-runner MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts",
"register-test-user": "MATRIX_USERNAME=user MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts",
"register-skills-writer": "MATRIX_USERNAME=skills_writer MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts",
"register-homepage-writer": "MATRIX_USERNAME=homepage_writer MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts",
"register-realm-users": "./scripts/register-realm-users.sh",
"register-test-admin": "MATRIX_IS_ADMIN=TRUE MATRIX_USERNAME=admin MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts",
"register-test-admin-and-token": "pnpm register-test-admin && ts-node --transpileOnly ./scripts/register-test-token.ts",
"register-all": "pnpm register-test-admin-and-token && pnpm register-realm-users && pnpm register-bot-user && pnpm register-test-user && pnpm register-skills-writer && pnpm register-homepage-writer",
"register-all": "pnpm register-test-admin-and-token && pnpm register-realm-users && pnpm register-bot-user && pnpm register-bot-runner && pnpm register-test-user && pnpm register-skills-writer && pnpm register-homepage-writer",
"test": "./scripts/test.sh",
"test:group": "./scripts/test.sh",
"wait": "sleep 10000000",
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const realmURL = Symbol.for('cardstack-realm-url');
export const relativeTo = Symbol.for('cardstack-relative-to');

export const aiBotUsername = 'aibot';
export const botRunnerUsername = 'bot-runner';

export const CardContextName = 'card-context';
export const CardCrudFunctionsContextName = 'card-crud-functions-context';
Expand Down
31 changes: 28 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.