From 8725f3e0221e6290aa51d791a0b71ac9e08ea30c Mon Sep 17 00:00:00 2001 From: Juni Date: Wed, 11 Dec 2024 13:13:07 -0600 Subject: [PATCH] First commit --- .env.example | 10 ++ .github/workflows/buildandpublishtoghcr.yml | 68 +++++++++++ .gitignore | 5 + Dockerfile | 35 ++++++ LICENSE | 19 +++ README.md | 3 + bun.lockb | Bin 0 -> 8930 bytes docker-compose.yml | 31 +++++ makefile | 23 ++++ package.json | 22 ++++ src/index.ts | 123 ++++++++++++++++++++ 11 files changed, 339 insertions(+) create mode 100644 .env.example create mode 100644 .github/workflows/buildandpublishtoghcr.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100755 bun.lockb create mode 100644 docker-compose.yml create mode 100644 makefile create mode 100644 package.json create mode 100644 src/index.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..25b6cb9 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +TRACKER_BSKY_HANDLE= +TRACKER_BSKY_PASSWORD= +DEBUG_LOG_ACTIVE=true +DEBUG_LOG_LEVEL=info + +JETSTREAM_URL='ws://jetstream:6008/subscribe' + +SESSION_DATA_PATH='/sessionData' + +USER_DID= \ No newline at end of file diff --git a/.github/workflows/buildandpublishtoghcr.yml b/.github/workflows/buildandpublishtoghcr.yml new file mode 100644 index 0000000..b874538 --- /dev/null +++ b/.github/workflows/buildandpublishtoghcr.yml @@ -0,0 +1,68 @@ +# +name: Create and publish the bot container + +# Configures this workflow to run every time a change is pushed to the branch called `release`. +on: + push: + branches: ['main'] + +# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. +jobs: + build-and-push-image: + runs-on: ubuntu-latest + # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. + permissions: + contents: read + packages: write + # + steps: + - name: Checkout repository + uses: actions/checkout@v4 + # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GHCR_PAT }} + # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + + # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. + # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. + # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64, linux/arm64 + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. + # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. + # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. +# - name: Build and push Docker image +# uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 +# with: +# context: . +# push: true +# tags: ${{ steps.meta.outputs.tags }} +# labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..345320f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +.idea +node_modules +sessionData +data \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3f07e35 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM oven/bun:latest as base +WORKDIR /usr/src/app + +# install dependencies into temp directory +# this will cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lockb /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile + +# install with --production (exclude devDependencies) +RUN mkdir -p /temp/prod +COPY package.json bun.lockb /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production + +# copy node_modules from temp directory +# then copy all (non-ignored) project files into the image +FROM install AS prerelease +COPY --from=install /temp/dev/node_modules node_modules +COPY . . + +# [optional] tests & build +ENV NODE_ENV=production +RUN bun test +RUN bun run build + +# copy production dependencies and source code into final image +FROM base AS release +COPY --from=install /temp/prod/node_modules node_modules +COPY --from=prerelease /usr/src/app/build/index.ts . +COPY --from=prerelease /usr/src/app/package.json . + +# run the app +USER bun +ENTRYPOINT [ "bun", "run", "index.ts" ] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d145cba --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 Juni + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b871023 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# user-tracker + +Tracks and reports on a users actions diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..f755aa4b03442584a676c69254da4c0974ce8d30 GIT binary patch literal 8930 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 literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dd1cb4e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +version: "3.8" +services: + bskybot: + depends_on: + - jetstream + build: . + restart: unless-stopped + volumes: + - ./sessionData:/sessionData + env_file: + - .env + networks: + - bun + + + jetstream: + image: "ghcr.io/juni-b-queer/jetstream-new:personal-branch" + container_name: jetstream + restart: unless-stopped + environment: + - CURSOR_FILE=/data/cursor.json + ports: + - "6008:6008" + volumes: + - ./data:/data + networks: + - bun + +networks: + bun: + driver: bridge diff --git a/makefile b/makefile new file mode 100644 index 0000000..d9bcde1 --- /dev/null +++ b/makefile @@ -0,0 +1,23 @@ +.PHONY: * + +dev: + bun run src/index.ts + +build: + docker compose build + +up: + docker compose up -d + +down: + docker compose down + +logs: + docker compose logs -f + +install: + bun install + + +link: + bun link bsky-event-handlers \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f760b04 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "user-tracker", + "description": "Tracks and reports on a users actions", + "version": "0.0.0", + "author": "Juni", + "module": "src/index.ts", + "type": "module", + "scripts": { + "build": "bun build --target=bun ./src/index.ts --outfile=./build/index.ts" + }, + "devDependencies": { + "bun-types": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "bsky-event-handlers": "2.1.0-beta.1", + "@atproto/api": "^0.13.19" + }, + "license": "MIT" +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..d8271f5 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,123 @@ +import { + BadBotHandler, + CreateSkeetHandler, + DebugLog, + GoodBotHandler, + HandlerAgent, + InputEqualsValidator, + JetstreamSubscription, + LogMessageAction, + ReplyingToBotValidator, + MessageHandler, + IntervalSubscription, + IntervalSubscriptionHandlers, + AbstractHandler, + IsSpecifiedTimeValidator, + CreateSkeetAction, + ActionTakenByUserValidator, + LogInputTextAction, + JetstreamEventCommit, + JetstreamSubject, + JetstreamRecord, TestValidator +} from 'bsky-event-handlers'; + +const testAgent = new HandlerAgent( + 'test-bot', + Bun.env.TRACKER_BSKY_HANDLE, + Bun.env.TRACKER_BSKY_PASSWORD +); + + +/** + * Jetstream Subscription setup + */ +let jetstreamSubscription: JetstreamSubscription; + + +let handlers = { + post: { + c: [ + new MessageHandler( + // @ts-ignore + [ActionTakenByUserValidator.make(Bun.env.USER_DID)], + [ + CreateSkeetAction.make("Aaron posted:", undefined, (handler: HandlerAgent, commit: JetstreamEventCommit): JetstreamSubject =>{ + return { + cid: MessageHandler.getCidFromMessage(handler, commit), + uri: MessageHandler.getUriFromMessage(handler, commit) + } + }), + LogInputTextAction.make("Post") + ], + testAgent + ), + GoodBotHandler.make(testAgent) + ] + }, + like: { + c: [ + new MessageHandler( + // @ts-ignore + [ActionTakenByUserValidator.make(Bun.env.USER_DID)], + [ + CreateSkeetAction.make("Aaron liked:", undefined, (handler: HandlerAgent, commit: JetstreamEventCommit): JetstreamSubject =>{ + return commit.commit.record.subject as JetstreamSubject; + }), + LogInputTextAction.make("Like") + ], + testAgent + ) + ] + }, + repost: { + c: [ + new MessageHandler( + // @ts-ignore + [ActionTakenByUserValidator.make(Bun.env.USER_DID)], + [ + CreateSkeetAction.make("Aaron reposted:", undefined, (handler: HandlerAgent, commit: JetstreamEventCommit): JetstreamSubject =>{ + return commit.commit.record.subject as JetstreamSubject; + }), + LogInputTextAction.make("Repost") + ], + testAgent + ) + ] + }, + block: { + c: [ + new MessageHandler( + // @ts-ignore + [ + ActionTakenByUserValidator.make(Bun.env.USER_DID) + ], + [ + CreateSkeetAction.make((handler: HandlerAgent, event: JetstreamEventCommit): string => { + const blockedDid = event.commit.record.subject + return "Aaron blocked a user: " + blockedDid; + }, undefined, undefined), + LogInputTextAction.make("Block"), + ], + testAgent + ) + ] + } +} + + + +async function initialize() { + await testAgent.authenticate() + jetstreamSubscription = new JetstreamSubscription( + handlers, + Bun.env.JETSTREAM_URL + ); + +} + +initialize().then(() =>{ + jetstreamSubscription.createSubscription() + DebugLog.info("INIT", 'Initialized!') +}); + +