automoddddd yeyyyyy
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

init :D !!

cool habdle 93104bce

+501
+2
.gitignore
··· 1 + node_modules 2 + .env
+37
default.nix
··· 1 + { 2 + lib, 3 + buildNpmPackage, 4 + fetchPnpmDeps, 5 + nodejs_22, 6 + pnpm_10, 7 + pnpmConfigHook, 8 + }: 9 + buildNpmPackage (finalAttrs: { 10 + pname = "automod"; 11 + version = "0.0.0"; 12 + 13 + src = ./.; 14 + 15 + npmDeps = null; 16 + pnpmDeps = fetchPnpmDeps { 17 + inherit (finalAttrs) 18 + pname 19 + version 20 + src 21 + ; 22 + pnpm = pnpm_10; 23 + fetcherVersion = 2; 24 + hash = "sha256-rb7Y4rbeIRy+9tzKliSmrCPY+nwgcf52lXjuOyBdTaA="; 25 + }; 26 + 27 + nativeBuildInputs = [ pnpm_10 ]; 28 + npmConfigHook = pnpmConfigHook; 29 + nodejs = nodejs_22; 30 + 31 + dontNpmBuild = true; 32 + 33 + meta = { 34 + license = lib.licenses.mit; 35 + mainProgram = "src/index.ts"; 36 + }; 37 + })
+27
flake.lock
··· 1 + { 2 + "nodes": { 3 + "nixpkgs": { 4 + "locked": { 5 + "lastModified": 1773628058, 6 + "narHash": "sha256-hpXH0z3K9xv0fHaje136KY872VT2T5uwxtezlAskQgY=", 7 + "owner": "nixos", 8 + "repo": "nixpkgs", 9 + "rev": "f8573b9c935cfaa162dd62cc9e75ae2db86f85df", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "nixos", 14 + "ref": "nixpkgs-unstable", 15 + "repo": "nixpkgs", 16 + "type": "github" 17 + } 18 + }, 19 + "root": { 20 + "inputs": { 21 + "nixpkgs": "nixpkgs" 22 + } 23 + } 24 + }, 25 + "root": "root", 26 + "version": 7 27 + }
+31
flake.nix
··· 1 + { 2 + description = "NodeJS Project Template"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 + }; 7 + 8 + outputs = 9 + { self, nixpkgs }: 10 + let 11 + forAllSystems = 12 + function: 13 + nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed ( 14 + system: function nixpkgs.legacyPackages.${system} 15 + ); 16 + in 17 + { 18 + packages = forAllSystems (pkgs: { 19 + automod = pkgs.callPackage ./default.nix { }; 20 + default = self.packages.${pkgs.stdenv.hostPlatform.system}.automod; 21 + }); 22 + 23 + nixosModules.default = import ./module.nix; 24 + 25 + devShells = forAllSystems (pkgs: { 26 + default = pkgs.callPackage ./shell.nix { }; 27 + }); 28 + 29 + overlays.default = final: _: { automod = final.callPackage ./default.nix { }; }; 30 + }; 31 + }
+45
module.nix
··· 1 + { 2 + lib, 3 + pkgs, 4 + config, 5 + ... 6 + }: 7 + 8 + let 9 + cfg = config.services.automod; 10 + in 11 + { 12 + options.services.automod = { 13 + enable = lib.mkEnableOption "meowderation automod"; 14 + 15 + package = lib.mkOption { 16 + type = lib.types.package; 17 + default = pkgs.callPackage ./default.nix { }; 18 + defaultText = lib.literalExpression "pkgs.callPackage ./default.nix { }"; 19 + description = "Package containing the automod application."; 20 + }; 21 + 22 + environmentFile = lib.mkOption { 23 + type = lib.types.nullOr lib.types.path; 24 + default = null; 25 + description = "Optional environment file containing USERNAME and PASSWORD."; 26 + }; 27 + }; 28 + 29 + config = lib.mkIf cfg.enable { 30 + systemd.services.automod = { 31 + description = "automod ! meow"; 32 + wantedBy = [ "multi-user.target" ]; 33 + wants = [ "network-online.target" ]; 34 + after = [ "network-online.target" ]; 35 + 36 + serviceConfig = { 37 + DynamicUser = true; 38 + Restart = "always"; 39 + RestartSec = 5; 40 + EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile; 41 + ExecStart = "${pkgs.nodejs_22}/bin/node --experimental-strip-types ${cfg.package}/src/index.ts"; 42 + }; 43 + }; 44 + }; 45 + }
+25
package.json
··· 1 + { 2 + "name": "meowderation_automod", 3 + "version": "1.0.0", 4 + "description": "", 5 + "main": "index.js", 6 + "type": "module", 7 + "scripts": { 8 + "test": "echo \"Error: no test specified\" && exit 1", 9 + "main": "node --experimental-strip-types src/index.ts" 10 + }, 11 + "keywords": [], 12 + "author": "", 13 + "license": "ISC", 14 + "packageManager": "pnpm@10.28.0", 15 + "dependencies": { 16 + "@atproto/api": "^0.19.3", 17 + "@skyware/jetstream": "^0.2.5", 18 + "atproto": "^0.0.1", 19 + "dotenv": "^17.3.1" 20 + }, 21 + "devDependencies": { 22 + "@types/node": "^25.5.0", 23 + "typescript": "^5.9.3" 24 + } 25 + }
+256
pnpm-lock.yaml
··· 1 + lockfileVersion: '9.0' 2 + 3 + settings: 4 + autoInstallPeers: true 5 + excludeLinksFromLockfile: false 6 + 7 + importers: 8 + 9 + .: 10 + dependencies: 11 + '@atproto/api': 12 + specifier: ^0.19.3 13 + version: 0.19.3 14 + '@skyware/jetstream': 15 + specifier: ^0.2.5 16 + version: 0.2.5 17 + atproto: 18 + specifier: ^0.0.1 19 + version: 0.0.1 20 + dotenv: 21 + specifier: ^17.3.1 22 + version: 17.3.1 23 + devDependencies: 24 + '@types/node': 25 + specifier: ^25.5.0 26 + version: 25.5.0 27 + typescript: 28 + specifier: ^5.9.3 29 + version: 5.9.3 30 + 31 + packages: 32 + 33 + '@atcute/atproto@3.1.10': 34 + resolution: {integrity: sha512-+GKZpOc0PJcdWMQEkTfg/rSNDAAHxmAUGBl60g2az15etqJn5WaUPNGFE2sB7hKpwi5Ue2h/L0OacINcE/JDDQ==} 35 + 36 + '@atcute/bluesky@3.3.0': 37 + resolution: {integrity: sha512-TrLnlxuD6F/D2ZYzJ3aCiRD0yiFuhmVsd6oULNzzr8V9Xzlufg0yxkRiGmbMiF2iI508y/MFi6vzo625301c5A==} 38 + 39 + '@atcute/lexicons@1.2.9': 40 + resolution: {integrity: sha512-/RRHm2Cw9o8Mcsrq0eo8fjS9okKYLGfuFwrQ0YoP/6sdSDsXshaTLJsvLlcUcaDaSJ1YFOuHIo3zr2Om2F/16g==} 41 + 42 + '@atcute/uint8array@1.1.1': 43 + resolution: {integrity: sha512-3LsC8XB8TKe9q/5hOA5sFuzGaIFdJZJNewC5OKa3o/eU6+K7JR6see9Zy2JbQERNVnRl11EzbNov1efgLMAs4g==} 44 + 45 + '@atcute/util-text@1.1.1': 46 + resolution: {integrity: sha512-JH0SxzUQJAmbOBTYyhxQbkkI6M33YpjlVLEcbP5GYt43xgFArzV0FJVmEpvIj0kjsmphHB45b6IitdvxPdec9w==} 47 + 48 + '@atproto/api@0.19.3': 49 + resolution: {integrity: sha512-G8YpBpRouHdTAIagi/QQIUZOhGd1jfBQWkJy9QfxAzjjEpPvaVOSk4e1S85QzGLm/xbzVONzGkmdtiOSfP6wVg==} 50 + 51 + '@atproto/common-web@0.4.18': 52 + resolution: {integrity: sha512-ilImzP+9N/mtse440kN60pGrEzG7wi4xsV13nGeLrS+Zocybc/ISOpKlbZM13o+twPJ+Q7veGLw9CtGg0GAFoQ==} 53 + 54 + '@atproto/lex-data@0.0.13': 55 + resolution: {integrity: sha512-7Z7RwZ1Y/JzBF/Tcn/I4UJ/vIGfh5zn1zjv0KX+flke2JtgFkSE8uh2hOtqgBQMNqE3zdJFM+dcSWln86hR3MQ==} 56 + 57 + '@atproto/lex-json@0.0.13': 58 + resolution: {integrity: sha512-hwLhkKaIHulGJpt0EfXAEWdrxqM2L1tV/tvilzhMp3QxPqYgXchFnrfVmLsyFDx6P6qkH1GsX/XC2V36U0UlPQ==} 59 + 60 + '@atproto/lexicon@0.6.2': 61 + resolution: {integrity: sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==} 62 + 63 + '@atproto/syntax@0.5.0': 64 + resolution: {integrity: sha512-UA2DSpGdOQzUQ4gi5SH+NEJz/YR3a3Fg3y2oh+xETDSiTRmA4VhHRCojhXAVsBxUT6EnItw190C/KN+DWW90kw==} 65 + 66 + '@atproto/xrpc@0.7.7': 67 + resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} 68 + 69 + '@skyware/jetstream@0.2.5': 70 + resolution: {integrity: sha512-fM/zs03DLwqRyzZZJFWN20e76KrdqIp97Tlm8Cek+vxn96+tu5d/fx79V6H85L0QN6HvGiX2l9A8hWFqHvYlOA==} 71 + 72 + '@standard-schema/spec@1.1.0': 73 + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} 74 + 75 + '@types/node@25.5.0': 76 + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} 77 + 78 + atproto@0.0.1: 79 + resolution: {integrity: sha512-iFbMYS8/UViX8bAKrSPD/SmXunEfarp3Y5OLpe/H5hp8cbjEm8t4KPpqrXPV55RzthAVtKj+vcM2d91wG/OLYA==} 80 + 81 + await-lock@2.2.2: 82 + resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} 83 + 84 + dotenv@17.3.1: 85 + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} 86 + engines: {node: '>=12'} 87 + 88 + esm-env@1.2.2: 89 + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 90 + 91 + event-target-polyfill@0.0.4: 92 + resolution: {integrity: sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ==} 93 + 94 + iso-datestring-validator@2.2.2: 95 + resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 96 + 97 + multiformats@9.9.0: 98 + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} 99 + 100 + partysocket@1.1.16: 101 + resolution: {integrity: sha512-d7xFv+ZC7x0p/DAHWJ5FhxQhimIx+ucyZY+kxL0cKddLBmK9c4p2tEA/L+dOOrWm6EYrRwrBjKQV0uSzOY9x1w==} 102 + peerDependencies: 103 + react: '>=17' 104 + peerDependenciesMeta: 105 + react: 106 + optional: true 107 + 108 + tiny-emitter@2.1.0: 109 + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} 110 + 111 + tlds@1.261.0: 112 + resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} 113 + hasBin: true 114 + 115 + tslib@2.8.1: 116 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 117 + 118 + typescript@5.9.3: 119 + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 120 + engines: {node: '>=14.17'} 121 + hasBin: true 122 + 123 + uint8arrays@3.0.0: 124 + resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 125 + 126 + undici-types@7.18.2: 127 + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} 128 + 129 + unicode-segmenter@0.14.5: 130 + resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} 131 + 132 + zod@3.25.76: 133 + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} 134 + 135 + snapshots: 136 + 137 + '@atcute/atproto@3.1.10': 138 + dependencies: 139 + '@atcute/lexicons': 1.2.9 140 + 141 + '@atcute/bluesky@3.3.0': 142 + dependencies: 143 + '@atcute/atproto': 3.1.10 144 + '@atcute/lexicons': 1.2.9 145 + 146 + '@atcute/lexicons@1.2.9': 147 + dependencies: 148 + '@atcute/uint8array': 1.1.1 149 + '@atcute/util-text': 1.1.1 150 + '@standard-schema/spec': 1.1.0 151 + esm-env: 1.2.2 152 + 153 + '@atcute/uint8array@1.1.1': {} 154 + 155 + '@atcute/util-text@1.1.1': 156 + dependencies: 157 + unicode-segmenter: 0.14.5 158 + 159 + '@atproto/api@0.19.3': 160 + dependencies: 161 + '@atproto/common-web': 0.4.18 162 + '@atproto/lexicon': 0.6.2 163 + '@atproto/syntax': 0.5.0 164 + '@atproto/xrpc': 0.7.7 165 + await-lock: 2.2.2 166 + multiformats: 9.9.0 167 + tlds: 1.261.0 168 + zod: 3.25.76 169 + 170 + '@atproto/common-web@0.4.18': 171 + dependencies: 172 + '@atproto/lex-data': 0.0.13 173 + '@atproto/lex-json': 0.0.13 174 + '@atproto/syntax': 0.5.0 175 + zod: 3.25.76 176 + 177 + '@atproto/lex-data@0.0.13': 178 + dependencies: 179 + multiformats: 9.9.0 180 + tslib: 2.8.1 181 + uint8arrays: 3.0.0 182 + unicode-segmenter: 0.14.5 183 + 184 + '@atproto/lex-json@0.0.13': 185 + dependencies: 186 + '@atproto/lex-data': 0.0.13 187 + tslib: 2.8.1 188 + 189 + '@atproto/lexicon@0.6.2': 190 + dependencies: 191 + '@atproto/common-web': 0.4.18 192 + '@atproto/syntax': 0.5.0 193 + iso-datestring-validator: 2.2.2 194 + multiformats: 9.9.0 195 + zod: 3.25.76 196 + 197 + '@atproto/syntax@0.5.0': 198 + dependencies: 199 + tslib: 2.8.1 200 + 201 + '@atproto/xrpc@0.7.7': 202 + dependencies: 203 + '@atproto/lexicon': 0.6.2 204 + zod: 3.25.76 205 + 206 + '@skyware/jetstream@0.2.5': 207 + dependencies: 208 + '@atcute/atproto': 3.1.10 209 + '@atcute/bluesky': 3.3.0 210 + '@atcute/lexicons': 1.2.9 211 + partysocket: 1.1.16 212 + tiny-emitter: 2.1.0 213 + transitivePeerDependencies: 214 + - react 215 + 216 + '@standard-schema/spec@1.1.0': {} 217 + 218 + '@types/node@25.5.0': 219 + dependencies: 220 + undici-types: 7.18.2 221 + 222 + atproto@0.0.1: {} 223 + 224 + await-lock@2.2.2: {} 225 + 226 + dotenv@17.3.1: {} 227 + 228 + esm-env@1.2.2: {} 229 + 230 + event-target-polyfill@0.0.4: {} 231 + 232 + iso-datestring-validator@2.2.2: {} 233 + 234 + multiformats@9.9.0: {} 235 + 236 + partysocket@1.1.16: 237 + dependencies: 238 + event-target-polyfill: 0.0.4 239 + 240 + tiny-emitter@2.1.0: {} 241 + 242 + tlds@1.261.0: {} 243 + 244 + tslib@2.8.1: {} 245 + 246 + typescript@5.9.3: {} 247 + 248 + uint8arrays@3.0.0: 249 + dependencies: 250 + multiformats: 9.9.0 251 + 252 + undici-types@7.18.2: {} 253 + 254 + unicode-segmenter@0.14.5: {} 255 + 256 + zod@3.25.76: {}
+27
shell.nix
··· 1 + { 2 + mkShellNoCC, 3 + 4 + # extra tooling 5 + eslint_d, 6 + prettierd, 7 + typescript, 8 + 9 + callPackage, 10 + }: 11 + let 12 + defaultPackage = callPackage ./default.nix { }; 13 + in 14 + mkShellNoCC { 15 + inputsFrom = [ defaultPackage ]; 16 + 17 + packages = [ 18 + eslint_d 19 + prettierd 20 + typescript 21 + ]; 22 + 23 + shellHook = '' 24 + eslint_d start # start eslint daemon 25 + eslint_d status # inform user about eslint daemon status 26 + ''; 27 + }
+51
src/index.ts
··· 1 + import { Agent, CredentialSession } from '@atproto/api' 2 + import { Jetstream } from '@skyware/jetstream'; 3 + import "dotenv/config"; 4 + 5 + async function main() { 6 + const session = new CredentialSession( 7 + new URL("https://tngl.sh") 8 + ); 9 + await session.login({ 10 + identifier: process.env.USERNAME!, 11 + password: process.env.PASSWORD!, 12 + }) 13 + 14 + const client = new Agent(session) 15 + const jetstream = new Jetstream(); 16 + 17 + jetstream.on("account", async (event) => { 18 + try { 19 + const profile = await client.getProfile({ 20 + actor: event.account.did 21 + }); 22 + 23 + if ( 24 + profile.data.handle.startsWith("mah-fam") || 25 + profile.data.handle.startsWith("mh-fam") 26 + ) { 27 + console.log(`labeling ${profile.data.handle} (${event.account.did})`); 28 + await client.tools.ozone.moderation.emitEvent({ 29 + event: { 30 + "$type": "tools.ozone.moderation.defs#modEventLabel", 31 + createLabelVals: ["mh-fam"] 32 + }, 33 + createdBy: client.assertDid, 34 + subject: { 35 + $type: "com.atproto.admin.defs#repoRef", 36 + did: event.account.did, 37 + }, 38 + modTool: { 39 + name: "automod", 40 + $type: "tools.ozone.moderation.defs#modTool", 41 + } 42 + }); 43 + } 44 + } catch (error) { 45 + console.error(`Error fetching profile for ${event.account.did}:`, error); 46 + } 47 + }); 48 + 49 + jetstream.start(); 50 + } 51 + main();