Skip to content

Commit

Permalink
feat: telegram / cron running on seperate enviroments (#3)
Browse files Browse the repository at this point in the history
* docker support

* rico cron oh yeah

* funciona el docking

* schedule cron on ci + telegram on seperate file

* docs: docker instructions
  • Loading branch information
panquequelol authored Nov 25, 2024
1 parent 2aad31d commit 302743b
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 107 deletions.
15 changes: 15 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules
Dockerfile*
docker-compose*
.dockerignore
.git
.gitignore
README.md
LICENSE
.vscode
Makefile
helm-charts
.env
.editorconfig
.idea
coverage*
9 changes: 6 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ on:
push:
branches:
- main
schedule: # daily at 8 am
- cron: "0 8 * * *"

jobs:
scrape:
runs-on: ubuntu-latest
Expand All @@ -15,7 +18,7 @@ jobs:
- uses: oven-sh/[email protected]
- name: Install dependencies
run: bun install
- name: Install browser
run: bunx playwright install --with-deps chromium
# - name: Install browser
# run: bunx playwright install --with-deps chromium
- name: execute
run: bun run src/main.ts
run: bun run src/cron.ts
5 changes: 5 additions & 0 deletions .tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"strict": true
}
}
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM oven/bun AS build

WORKDIR /app

COPY bun.lockb .
COPY package.json .

RUN bun install --frozen-lockfile

COPY src ./src

# compile everything to a binary called cli which includes the bun runtime
RUN bun build ./src/server.ts --compile --outfile cli

# use a smaller image without bun
FROM ubuntu:22.04

WORKDIR /app

# copy the compiled binary from the build image
COPY --from=build /app/cli /app/cli

# execute the binary!
CMD ["/app/cli"]
23 changes: 7 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
# blatanly stealing stuff
# osusach pegas

ofertas de la gente del dcc y de getonboard

# tech

- ts
- bun
- tursodb
- playwright
- zod
ladron de pegas usando bun + ts

# setup

Expand All @@ -31,10 +23,9 @@ TURSO_URL=
TURSO_TOKEN=
```

and that's it
# docker

# usage

1. `bun install`
2. `bun start`
3. wait for the magic to happen
```zsh
docker build --no-cache --pull -t pegas-osusach .
docker run -p 3000:3000 --rm -it pegas-osusach
```
Binary file modified bun.lockb
Binary file not shown.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
{
"scripts": {
"start": "tsx src/index.ts"
"start": "bun --env-file=.env src/server.ts"
},
"dependencies": {
"@libsql/client": "^0.3.6",
"croner": "^9.0.0",
"eslint-plugin-neverthrow": "^1.1.4",
"grammy": "^1.32.0",
"neverthrow": "^8.1.0",
"pino": "^9.5.0",
"playwright": "^1.39.0",
"zod": "^3.22.4"
},
"devDependencies": {
"tsx": "^3.12.2"
}
},
"packageManager": "^[email protected]"
}
33 changes: 33 additions & 0 deletions src/cron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { save } from "./lib/save";
import { getGetonboardJobs } from "./sources/getonboard";

// TODO: normalize for multiple sources and use neverthrow
async function cron() {
const [getonboard] = await Promise.allSettled([getGetonboardJobs()]);

if (getonboard.status === "rejected")
console.error(`couldn't steal from getonboard ${getonboard.reason}`);

const jobs = [...(getonboard.status === "fulfilled" ? getonboard.value : [])];

if (jobs.length === 0) {
console.info("no new jobs found");
return;
}

const results = await save(jobs);
if (results.isErr()) {
console.error(`coudn't save new jobs, ${results.error}`);
} else {
console.info(
"saved " +
results.value.fulfilled +
", " +
results.value.rejected +
" failed"
);
}
return;
}

cron();
2 changes: 1 addition & 1 deletion src/lib/db.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createClient } from "@libsql/client";
import { createClient } from "@libsql/client/web";
import { Result } from "neverthrow";

export const createConnection = Result.fromThrowable(() =>
Expand Down
1 change: 0 additions & 1 deletion src/lib/save.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { createClient } from "@libsql/client";
import { Job } from "../sources/schema";
import keywordList from "../keywordList";
import { createConnection } from "./db";
Expand Down
39 changes: 39 additions & 0 deletions src/lib/telegram.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Bot } from "grammy";
import { ResultAsync } from "neverthrow";
import { jobSchema } from "../sources/schema";
import { save } from "./save";

const TOKEN = process.env.TELEGRAM_BOT_TOKEN;
export async function unsafeDCCListener() {
if (!TOKEN) throw new Error("no telegram bot token found in .env");
const bot = new Bot(TOKEN);
bot.on("channel_post", (ctx) => {
if (ctx.chat.title === "DCCEmpleo") {
const job = jobSchema.safeParse({
id: `${ctx.channelPost.message_id}_DCCEmpleo`,
content: ctx.channelPost.text,
date: new Date(
new Date().toLocaleString("en-US", { timeZone: "America/Santiago" })
),
source: "DCC_TELEGRAM",
});

if (job.success) {
save([job.data]);
} else {
console.error(job.error);
}
}
});

await bot.start({
onStart() {
console.info("telegram bot is runnning");
},
});
}

export const dccListener = ResultAsync.fromThrowable(
unsafeDCCListener,
(error) => (error instanceof Error ? error : new Error("Unknown error"))
);
41 changes: 0 additions & 41 deletions src/main.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { dccListener } from "./lib/telegram";

async function server() {
console.log("runtime: " + process.release.name);
const listener = await dccListener();
if (listener.isErr()) {
console.error(listener.error.message);
return;
}
}

server();
40 changes: 0 additions & 40 deletions src/sources/dcc.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/sources/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { z } from "zod";

const source = ["DCC_TELEGRAM", "GETONBOARD_CHILE"] as const;
export type Source = typeof source;
export const JobSchema = z.object({
id: z.string(),
export const jobSchema = z.object({
id: z.union([z.string(), z.number()]).transform(String), // Ensures id is always a string
date: z.date(),
content: z.string(),
source: z.enum(source),
});
export type Job = z.infer<typeof JobSchema>;
export type Job = z.infer<typeof jobSchema>;

0 comments on commit 302743b

Please sign in to comment.