First implementation

This commit is contained in:
2026-08-22 15:24:48 -04:00
parent 80b8ddbd3f
commit f714fad2d4
16 changed files with 461 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
# flyctl launch added from .gitignore
# Environment
**/.env
# Dependencies
**/node_modules
# Build output
**/dist
# OS
**/.DS_Store
**/Thumbs.db
fly.toml
+14
View File
@@ -1 +1,15 @@
# Environment
.env .env
# Dependencies
node_modules/
# Bun
bun.lock
# Build output
dist/
# OS
.DS_Store
Thumbs.db
+106
View File
@@ -0,0 +1,106 @@
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
// import .css files directly and it works
import './index.css';
import { createRoot } from "react-dom/client";
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
+13
View File
@@ -0,0 +1,13 @@
FROM oven/bun:1-alpine AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production
FROM oven/bun:1-alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY tsconfig.json ./
COPY src ./src
USER bun
CMD ["bun", "src/index.ts"]
+15
View File
@@ -0,0 +1,15 @@
# kaczynski-bot-2.0
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run
```
This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
+21
View File
@@ -0,0 +1,21 @@
# fly.toml app configuration file generated for kaczynski-bot-2-0 on 2026-05-16T14:37:01-04:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'kaczynski-bot-2-0'
primary_region = 'iad'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 1
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
memory_mb = 1024
View File
+14
View File
@@ -0,0 +1,14 @@
{
"name": "kaczynski-bot-2.0",
"private": true,
"devDependencies": {
"@types/bun": "latest",
"@types/node": "^25.8.0"
},
"peerDependencies": {
"typescript": "^5.9.3"
},
"dependencies": {
"discord.js": "^14.26.4"
}
}
+40
View File
@@ -0,0 +1,40 @@
import { Client, Events, GatewayIntentBits } from 'discord.js';
import { CommandService } from './services/cmd/service';
import RoleService from './services/role/service';
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
// Services
const commandService = new CommandService(client);
const roleService = new RoleService(client);
client.once(Events.ClientReady, async (readyClient) => {
// Initialize & validate
await commandService.init();
roleService.validate();
// Ready!
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
});
client.on("guildMemberAdd", async (guildMember) => {
await roleService.addStarterRoles(guildMember);
});
client.on("messageCreate", async (message) => {
if (message.author.bot) return;
const command = commandService.parse(message);
if (command) command.execute(message);
});
Bun.cron("0 */6 * * *", async () => {
for (const guild of client.guilds.cache.values()) {
const lurkerRole = roleService.getRoleByName(guild, roleService.ROLES["LURKER"]);
if (!lurkerRole) continue;
await guild.members.prune({ days: 5, roles: [lurkerRole] });
}
});
client.login(Bun.env.DISCORD_TOKEN);
+14
View File
@@ -0,0 +1,14 @@
import type { Message } from "discord.js";
import type { Command } from "../service";
import Chan from "../../../util/chan";
export default {
name: "chan",
description: "Get random media from a 4chan board. Usage: .chan <board> [search]",
execute: async (message: Message) => {
const [, board, search] = message.content.split(/\s+/);
if (!board) return message.reply("Usage: .chan <board> [search]");
if (Chan.isNsfwBoard(board)) return message.reply("Fuck off nigger");
await message.reply(await Chan.getMediaLink(board, search));
}
} as Command;
+13
View File
@@ -0,0 +1,13 @@
import type { Message } from "discord.js";
import type { Command } from "../service";
import Chan from "../../../util/chan";
export default {
name: "comfy",
description: "Get comfy content from 4chan",
execute: async (message: Message) => {
const boards = ["wsg", "wg"];
const board = boards[Math.floor(Math.random() * boards.length)] ?? "wsg";
await message.reply(await Chan.getMediaLink(board, "comfy"));
}
} as Command;
+51
View File
@@ -0,0 +1,51 @@
import type { Message } from "discord.js";
import type { Command } from "../service";
export default {
name: "redpillme",
description: "Receive wisdom from the Unabomber",
execute: async (message: Message) => {
const quote = quotes[Math.floor(Math.random() * quotes.length)];
await message.reply(quote!);
}
} as Command;
const quotes = [
"Our society tends to regard as a sickness any mode of thought or behavior that is inconvenient for the system and this is plausible because when an individual doesn't fit into the system it causes pain to the individual as well as problems for the system. Thus the manipulation of an individual to adjust him to the system is seen as a cure for a sickness and therefore as good.",
"Those who are most sensitive about 'politically incorrect' terminology are not the average black ghetto-dweller, Asian immigrant, abused woman or disabled person, but a minority of activists, many of whom do not even belong to any 'oppressed' group but come from privileged strata of society.",
"The conservatives are fools: They whine about the decay of traditional values, yet they enthusiastically support technological progress and economic growth. Apparently it never occurs to them that you can't make rapid, drastic changes in the technology and the economy of a society without causing rapid changes in all other aspects of the society as well, and that such rapid changes inevitably break down traditional values.",
"It is obvious that [leftists] are not cool-headed logicians systematically analyzing the foundations of knowledge. They are deeply involved emotionally in their attack on truth and reality.",
"A chorus of voices exhorts kids to study science. No one stops to ask whether it is inhumane to force adolescents to spend the bulk of their time studying subjects most of them hate. When skilled workers are put out of a job by technical advances and have to undergo \"retraining, \" no one asks whether it is humiliating for them to be pushed around in this way. It is simply taken for granted that everyone must bow to technical necessity, and for good reason: If human needs were put before technical necessity there would be economic problems, unemployment, shortages or worse. The concept of \"mental health\" in our society is defined largely by the extent to which an individual behaves in accord with the needs of the system and does so without showing signs of stress.",
"The leftist is anti-individualistic... He is not the sort of person who has an inner sense of confidence in his own ability to solve his own problems and satisfy his own needs.",
"There is no law that says we have to go to work every day and follow our employer's orders. Legally there is nothing to prevent us from going to live in the wild like primitive people or from going into business for ourselves. But in practice there is very little wild country left, and there is room in the economy for only a limited number of small business owners. Hence most of us can survive only as someone else's employee.",
"History is made by active, determined minorities, not by the majority, which seldom has a clear and consistent idea of what it really wants.",
"The degree of personal freedom that exists in a society is determined more by the economic and technological structure of the society than by its laws or its form of government. Most of the Indian nations of New England were monarchies, and many of the cities of the Italian Renaissance were controlled by dictators. But in reading about these societies one gets the impression that they allowed far more personal freedom than our society does. In part this was because they lacked efficient mechanisms for enforcing the ruler's will: There were no modern, well-organised police forces, no rapid long-distance communications, no surveillance cameras, no dossiers of information about the lives of average citizens. Hence it was relatively easy to evade control.",
"A surrogate activity is an activity that is directed toward an artificial goal that the individual pursues for the sake of the \"fulfillment\" that he gets from pursuing the goal, not because he needs to attain the goal itself. For instance, there is no practical motive for building enormous muscles, hitting a little ball into a hole or acquiring a complete series of postage stamps. Yet many people in our society devote themselves with passion to bodybuilding, golf or stamp-collecting. Some people are more \"other-directed\" than others, and therefore will more readily attach importance to a surrogate activity simply because the people around them treat it as important or because society tells them it is important. That is why some people get very serious about essentially trivial activities such as sports, or bridge, or chess, or arcane scholarly pursuits, whereas others who are more clear-sighted never see these things as anything but the surrogate activities that they are, and consequently never attach enough importance to them to satisfy their need for the power process in that way.",
"Modern leftish philosophers tend to dismiss reason, science, objective reality and to insist that everything is culturally relative. More importantly, the leftist hates science and rationality because they classify certain beliefs as true (i.e., successful, superior) and other beliefs as false (i.e., failed, inferior). The leftist's feelings of inferiority run so deep that he cannot tolerate any classification of some things as successful or superior and other things as failed or inferior. This also underlies the rejection by many leftists of the concept of mental illness and of the utility of IQ tests. Leftists are antagonistic to genetic explanations of human abilities or behavior because such explanations tend to make some persons appear superior or inferior to others. Leftists prefer to give society the credit or blame for an individual's ability or lack of it. Thus if a person is \"inferior\" it is not his fault, but society's, because he has not been brought up properly.",
"The System has played a trick on today's would-be revolutionaries and rebels. The trick is so cute that if it had been consciously planned one would have to admire it for its almost mathematical elegance.",
"Leftists of the oversocialized type tend to be intellectuals or members of the upper-middle class. Notice that university intellectuals constitute the most highly socialized segment of our society and also the most leftwing segment.",
"There is good reason to believe that primitive man suffered from less stress and frustration and was better satisfied with his way of life than modern man is.",
"In modern industrial society only minimal effort is necessary to satisfy one's physical needs. It is enough to go through a training program to acquire some petty technical skill, then come to work on time and exert the very modest effort needed to hold a job. The only requirements are a moderate amount of intelligence and, most of all, simple OBEDIENCE.",
"Manifesto. Read my Manifesto. I've written a Manifesto. It's all in the Manifesto!",
"Art forms that appeal to [leftists] tend to focus on ... defeat and despair ... as if there were no hope of accomplishing anything through rational calculation.",
"Leftists may claim that their activism is motivated by compassion or by moral principles, and moral principle does play a role for the leftist of the oversocialized type. But compassion and moral principle cannot be the main motives for leftist activism. Hostility is too prominent a component of leftist behavior; so is the drive for power. Moreover, much leftist behavior is not rationally calculated to be of benefit to the people whom the leftists claim to be trying to help.",
"It would be better to dump the whole stinking system and take the consequences.",
"Our society tends to regard as a sickness any mode of thought or behavior that is inconvenient for the system and this is plausible because when an individual doesn't fit into the system it causes pain to the individual as well as problems for the system. Thus the manipulation of an individual to adjust him to the system is seen as a cure for a sickness and therefore as good.",
"These engineered human beings may be happy in such a society, but they most certainly will not be free. They will have been reduced to the status of domestic animals.",
"The leftist is antagonistic to the concept of competition because, deep inside, he feels like a loser.",
"Modern man must satisfy his need for the power process largely through pursuit of the artificial needs created by the advertising and marketing industry, and through surrogate activities.",
"The industrial-technological system may survive or it may break down. If it survives, it MAY eventually achieve a low level of physical and psychological suffering, but only after passing through a long and very painful period of adjustment and only at the cost of permanently reducing human beings and many other living organisms to engineered products and mere cogs in the social machine. Furthermore, if the system survives, the consequences will be inevitable: There is no way of reforming or modifying the system so as to prevent it from depriving people of dignity and autonomy.",
"If the system breaks down the consequences will still be very painful. But the bigger the system grows the more disastrous the results of its breakdown will be, so if it is to break down it had best break down sooner rather than later.",
"Our lives depend on whether safety standards at a nuclear power plant are properly maintained; on how much pesticide is allowed to get into our food or how much pollution into our air; on how skillful (or incompetent) our doctor is; whether we lose or get a job may depend on decisions made by government economists or corporation executives; and so forth. Most individuals are not in a position to secure themselves against these threats to more than a very limited extent.",
"Leftists tend to hate anything that has an image of being strong, good and successful. They hate America, they hate Western civilization, they hate white males, they hate rationality.",
"For most people identification with a large organization or a mass movement does not fully satisfy the need for power.",
"Many people put into their work far more effort than is necessary to earn whatever money and status they require, and this extra effort constitutes a surrogate activity. This extra effort, together with the emotional investment that accompanies it, is one of the most potent forces acting toward the continual development and perfecting of the system, with negative consequences for individual freedom.",
"Freedom means being in control (either as an individual or as a member of a SMALL group) of the life-and-death issues of one's existence; food, clothing, shelter and defense against whatever threats there may be in one's environment. Freedom means having power; not the power to control other people but the power to control the circumstances of one's own life.",
"Our use of mass entertainment is 'optional': No law requires us to watch television, listen to the radio, read magazines. Yet mass entertainment is a means of escape and stress-reduction on which most of us have become dependent.",
"But it is obvious that modern leftish philosophers are not simply cool-headed logicians systematically analyzing the foundations of knowledge.",
"When someone interprets as derogatory almost anything that is said about him (or about groups with whom he identifies) we conclude that he has inferiority feelings or low self-esteem. This tendency is pronounced among minority rights activists, whether or not they belong to the minority groups whose rights they defend. They are hypersensitive about the words used to designate minorities and about anything that is said concerning minorities.",
"Imagine a society that subjects people to conditions that make them terribly unhappy, then gives them drugs to take away their unhappiness. Science fiction? It is already happening to some extent in our own society. It is well known that the rate of clinical depression has been greatly increasing in recent decades. We believe that this is due to disruption of the power process, as explained in paragraphs 59-76. But even if we are wrong, the increasing rate of depression is certainly the result of SOME conditions that exist in today's society. Instead of removing the conditions that make people depressed, modern society gives them antidepressant drugs. In effect, antidepressants are a means of modifying an individual's internal state in such a way as to enable him to tolerate social conditions that he would otherwise find intolerable.",
"Up to a point, having fun is good for you. But it's not an adequate substitute for serious, purposeful activity. For lack of this kind of activity people in our society get bored. They try to relieve their boredom by having fun. They seek new kicks, new thrills, new adventures. They masturbate their emotions by experimenting with new religions, new art-forms, travel, new cultures, new philosophies, new technologies. But still they are never satisfied, they always want more, because all of these activities are purposeless. People don't realize that what they really lack is serious, practical, purposeful work—work that is under their own control and is directed to the satisfaction of their own most essential, practical needs.",
"Today people live more by virtue of what the system does FOR them or TO them than by virtue of what they do for themselves.",
"The argument that \"people now have more freedom than ever\" is based on the fact that we are allowed to do almost anything we please as long as it has no practical consequences. Where our actions have practical consequences that may be of concern to the system, our behavior, generally speaking, is closely regulated.",
];
+33
View File
@@ -0,0 +1,33 @@
import type { Client, Message } from "discord.js";
import { readdir } from "fs/promises";
const CMD_PREFIX = ".";
export interface Command {
name: string,
description: string,
execute: (message: Message) => void | Promise<void>
}
export class CommandService {
private client: Client
private registry: Record<string, Command> = {}
constructor(client: Client) {
this.client = client;
}
async init() {
const commands = await Promise.all(
(await readdir(`${import.meta.dir}/registry`)).map(async file => (await import(`./registry/${file}`)).default as Command)
);
for (const command of commands)
this.registry[command.name] = command;
};
parse(message: Message): Command | undefined {
if (!message.content.startsWith(CMD_PREFIX)) return;
const name = message.content.slice(CMD_PREFIX.length).split(/\s+/)[0];
return this.registry[name ?? ""];
}
}
+42
View File
@@ -0,0 +1,42 @@
import type { Client, Guild, GuildMember } from "discord.js";
export default class RoleService {
ROLES = {
"ROBOT": "robot",
"NORMALFAG": "normalfag",
"FEMOID": "femoid",
"GAY": "gay",
"TROID": "troid",
"LURKER": "lurker",
"NEWFAG": "newfag",
"LIMBO": "limbo"
};
private client: Client
private starterRoles = [this.ROLES["LURKER"], this.ROLES["NEWFAG"]];
constructor(client: Client) {
this.client = client;
}
validate(): void {
this.client.guilds.cache.forEach(guild => {
const missing = Object.values(this.ROLES).filter(name => !guild.roles.cache.find(r => r.name === name));
if (missing.length) console.error(`${guild.name} is missing roles: ${missing.join(",")}`);
});
}
async addStarterRoles(guildMember: GuildMember) {
for (const starterRole of this.starterRoles) {
const role = this.getRoleByName(guildMember.guild, starterRole);
if (!role) continue;
await guildMember.roles.add(role);
}
}
getRoleByName(guild: Guild, roleName: string) {
return guild.roles.cache.find(role => role.name === roleName);
}
}
+42
View File
@@ -0,0 +1,42 @@
export default class Chan {
private static readonly nsfwBoards = new Set([
"b", "d", "e", "h", "hc", "hr", "gif", "s", "t", "u", "r9k", "soc", "vr", "w",
"i", "ic", "r", "p", "cgl", "cm", "n", "vip", "qa", "y", "trash", "hm", "aco", "lgbt"
]);
static isNsfwBoard(board: string): boolean {
return this.nsfwBoards.has(board);
}
static async getMediaLink(board: string, search = "none"): Promise<string> {
const catalog = await fetch(`https://a.4cdn.org/${board}/catalog.json`).then(r => r.json()) as any[];
const threadIds: number[] = [];
for (const page of catalog) {
for (const thread of page.threads) {
if (search !== "none") {
const sub = (thread.sub ?? "").toLowerCase();
const com = (thread.com ?? "").toLowerCase();
if (sub.includes(search) || com.includes(search))
threadIds.push(thread.no);
} else {
threadIds.push(thread.no);
}
}
}
if (threadIds.length === 0) return "No Matches";
const threadId = threadIds[Math.floor(Math.random() * threadIds.length)];
const thread = await fetch(`https://a.4cdn.org/${board}/thread/${threadId}.json`).then(r => r.json()) as { posts: any[] };
const links: string[] = [];
for (const post of thread.posts) {
if ("filename" in post)
links.push(`https://i.4cdn.org/${board}/${post.tim}${post.ext}`);
}
if (links.length === 0) return "No Matches";
return links[Math.floor(Math.random() * links.length)] ?? "No Matches";
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}