{"id":13770156,"url":"https://github.com/SleeplessOne1917/lemmy-bot","last_synced_at":"2025-05-11T02:35:07.348Z","repository":{"id":123903318,"uuid":"576026751","full_name":"SleeplessOne1917/lemmy-bot","owner":"SleeplessOne1917","description":"A bot library for Lemmy, the fediverse link aggregator.","archived":false,"fork":false,"pushed_at":"2025-04-23T04:31:44.000Z","size":537,"stargazers_count":97,"open_issues_count":12,"forks_count":11,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-23T05:27:15.044Z","etag":null,"topics":["bot","bots","fediverse","fediverse-bot","lemmy","lemmy-bot","typescript","typescript-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/lemmy-bot","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SleeplessOne1917.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-12-08T20:57:12.000Z","updated_at":"2025-04-23T04:30:33.000Z","dependencies_parsed_at":"2023-03-23T22:47:32.508Z","dependency_job_id":"b804636c-a59b-4135-9cce-e3627f409b94","html_url":"https://github.com/SleeplessOne1917/lemmy-bot","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessOne1917%2Flemmy-bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessOne1917%2Flemmy-bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessOne1917%2Flemmy-bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SleeplessOne1917%2Flemmy-bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SleeplessOne1917","download_url":"https://codeload.github.com/SleeplessOne1917/lemmy-bot/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253507367,"owners_count":21919200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","bots","fediverse","fediverse-bot","lemmy","lemmy-bot","typescript","typescript-library"],"created_at":"2024-08-03T17:00:34.863Z","updated_at":"2025-05-11T02:35:07.332Z","avatar_url":"https://github.com/SleeplessOne1917.png","language":"TypeScript","funding_links":[],"categories":["Projects"],"sub_categories":["Tools"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/l/lemmy-bot?style=plastic\" alt=\"License\" /\u003e\n  \u003cimg src=\"https://img.shields.io/librariesio/github/SleeplessOne1917/lemmy-bot?style=plastic\" alt=\"Dependencies up to date\" /\u003e\n  \u003cimg src=\"https://img.shields.io/github/stars/SleeplessOne1917/lemmy-bot?style=plastic\" alt=\"Github stars\" /\u003e\n  \u003cimg src=\"https://img.shields.io/npm/v/lemmy-bot?style=plastic\" alt=\"npm version\" /\u003e\n  \u003cimg src=\"https://img.shields.io/github/last-commit/SleeplessOne1917/lemmy-bot/main?style=plastic\" alt=\"Last commit\" /\u003e\n  \u003cimg src=\"https://img.shields.io/github/issues/SleeplessOne1917/lemmy-bot?style=plastic\" alt=\"Issues\" /\u003e\n  \u003cimg src=\"https://img.shields.io/github/issues-pr-raw/SleeplessOne1917/lemmy-bot?style=plastic\" alt=\"Open pull requests\" /\u003e\n  \u003cimg src=\"https://img.shields.io/npm/dm/lemmy-bot\" alt=\"Downloads per month\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/-Typescript-white?logo=typescript\u0026style=plastic\" alt=\"Typescript\"\u003e\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/LemmyNet/lemmy\" rel=\"noopener\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/LemmyNet/lemmy-ui/main/src/assets/icons/favicon.svg\" alt=\"Lemmy logo\" width=\"250px\" height=\"250px\"/\u003e\n  \n  \u003ch1 align=\"center\"\u003elemmy-bot\u003c/h1\u003e\n  \u003cp align=\"center\"\u003eLibrary to make it easier to make bots for \u003ca href=\"https://join-lemmy.org/\"\u003eLemmy\u003c/a\u003e, the fediverse forum/link aggregator.\u003c/p\u003e\n\u003c/div\u003e\n\n## Features\n\n- Respond to different events that happen in lemmy, such as posts, comments, and modlog actions\n- Perform actions on a schedule\n- Supports most actions a regular Lemmy account can make, including moderator and admin actions\n\n## Installation\n\n**Note**: This library only works with Node versions 18 and above.\n\nRun\n\n```\nnpm install lemmy-bot\n```\n\nor\n\n```\nyarn add lemmy-bot\n```\n\nor\n\n```\npnpm install lemmy-bot\n```\n\n## Lemmy Versions\n\nFor lemmy versions 0.17.x, use lemmy-bot 0.3.9 and lower.\n\nFor lemmy versions 0.18.x, use lemmy-bot 0.4.x.\n\nFor lemmy versions 0.19.x, use lemmy-bot 0.5.x or 0.6.x.\n\n## Documentation\n\n### LemmyBot\n\nCreate a bot by newing up a `LemmyBot`\n\n```typescript\nimport LemmyBot from 'lemmy-bot';\n\nconst bot = new LemmyBot({\n  // Pass configuration options here\n});\n```\n\nCalling `bot.start()` will start the bot. Calling `bot.stop()` will stop it.\n\n---\n\n### LemmyBotOptions\n\nWhat your bot does is determined by the options passed to the constructor. They are as follows:\n\n#### `credentials`\n\nLog in credentials for the bot. Accepts an object with `username` and `password` properties. If not provided, the bot can still poll the instance for items like posts, comments, and modlog actions, but it will not be able to perform actions that require an account.\n\n#### `instance` **REQUIRED**\n\nThe Lemmy instance your bot will connect to. Only pass the domain name of the instance, e.g. if the bot is supposed to be on lemmy.ml, pass `'lemmy.ml'`, **not** `'https://lemmy.ml'`.\n\n#### `connection`\n\nOptions for the bot's connection. It is an object with the following properties:\n\n- `minutesBeforeRetryConnection`: If the bot's connection closes, the bot will wait this many minutes before opening another connection and trying again. Default value is 5. If you don't want the bot to attempt to reconnect, pass in `false`.\n- `secondsBetweenPolls`: Number of seconds between HTTP requests the bot will make to check for items to handle. Default value is 30.\n- `minutesUntilReprocess`: If the bot can to potentially handle the same item more than once (e.g. polling posts by top day every minute and replying to any with more than 25 points), `minutesUntilReprocess` specifies how many minutes must pass until an item is valid for reprocessing. If this value is undefined, items will not be reprocessed at all. Default value is undefined.\n  **Note**: It is possible that an item that is valid for reprocessing will not be handled again. Taking the example from before and polling every day instead of every minute, a post from the day before that is valid for reprocessing might not show up in the current day's top posts.\n\n#### `handlers`\n\nOptions that control what the bot does when encountering a certain item. The following is a simple example to show how handlers work:\n\n```typescript\nimport LemmyBot from 'lemmy-bot';\n\nconst bot = new LemmyBot({\n  handlers: {\n    post: (res) =\u003e {\n      console.log(res.postView.post.name);\n    }\n  }\n});\n\nbot.start();\n```\n\nIn the previous example, the bot polls the instance for posts and logs the name of each one.\n\nEach handler can accept an object with the following properties:\n\n- `handle`: Function to run to handle an item. Acccepts the item being handled as an argument.\n- `secondsBetweenPolls`: Does the same thing as the one from [connection](#connection). Any value provided will override the value set in [connection](#connection) for handling items of a given type.\n- `minutesUntilReprocess`: Does the same thing as the one from [connection](#connection). Any value provided will override the value set in [connection](#connection) for handling items of a given type.\n\nSome handlers accept more options.\n\nIf using the default values for `secondsBetweenPolling` and `minutesUntilReprocess`, the handle function can be used instead of the object.\n\nThe handle function receives an object that has the following props as an argument:\n\n- `botActions`: Different actions the bot can perform. More on bot actions in the [bot actions](#bot-actions) sections.\n- `preventReprocess`: Call if the item being handled should not be handled again, even if `minutesUntilReprocess` is set.\n- `reprocess`: Mark the item being handled as able to be reprocessed, even if `minutesUntilReprocess` is not set.\n- item (property name varies depending on handler): The item being handled.\n\nThe following are the properties that can be set on `handlers`:\n\n- `comment`: Handle function has `commentView` in the argument object. Handler options also accept `sort` property of type `CommentSortType`.\n- `post`: Handle function has `postView` in the argument object. Handler options also accept `sort` property of type `SortType`.\n- `privateMessage`: Handle function has `messageView` in the argument object.\n- `comment`: Handle function has `commentView` in the argument object.\n- `registrationApplication`: Handle function has `applicationView` in the argument object.\n- `mention`: Handle function has `mentionView` in the argument object.\n- `reply`: Handle function has `replyView` in the argument object.\n- `commentReport`: Handle function has `reportView` in the argument object.\n- `postReport`: Handle function has `reportView` in the argument object.\n- `privateMessageReport`: Handle function has `reportView` in the argument object.\n- `modRemovePost`: Handle function has `removedPostView` in the argument object.\n- `modLockPost`: Handle function has `lockedPostView` in the argument object.\n- `modFeaturePost`: Handle function has `featurePostView` in the argument object.\n- `modRemoveComment`: Handle function has `removedCommentView` in the argument object.\n- `modRemoveCommunity`: Handle function has `removedCommunityView` in the argument object.\n- `modBanFromCommunity`: Handle function has `banView` in the argument object.\n- `modAddModToCommunity`: Handle function has `modAddedToCommunityView` in the argument object.\n- `modTransferCommunity`: Handle function has `modTransferredToCommunityView` in the argument object.\n- `modAddAdmin`: Handle function has `addedAdminView` in the argument object.\n- `modBanFromSite`: Handle function has `banView` in the argument object.\n\n#### `federation`\n\nOptions for handling federated instances. Can be one of:\n\n- `'local'`: Only handle items on the bot's local instance. This is the default setting.\n- `'all'`: Handle items on any instance, both local and federated.\n- object with the following properties:\n  - `allowList`: List of instances the bot is allowed to handle items from.\n  - `blockList`: List of instances the bot is not allowed to handle ites from.\n\nA bot cannot set both a block list and an allow list.\nEntries `allowList` and `blockList` can either be strings or objects. If the entry is a string, all items on the instance named by the string will be allowed/blocked. If an object, it must have the following shape:\n\n- `instance`: Domain name of the instance to allow/block from.\n- `communities`: List of community names on the instance that should be allowed/blocked.\n\n**Note**: If you want to limit checking on your local instance to only a few communities, do not pass `local`; instead, pass an object with an `allowList` with your local instance domain and desired communities.\n\n#### `schedule`\n\nTask object or list of task objects. Task objects have the following properties:\n\n- `cronExpression`: String expression that controls when the task runs. See [node-cron](https://www.npmjs.com/package/cron) for valid expression syntax.\n- `doTask`: Run the task. Takes [bot actions](#bot-actions) as an argument.\n- `timezone`: String stating the timezone the schedule should be in. See [here](https://momentjs.com/timezone/) for supported timezones.\n- `runAtStart`: Boolean value for whether or not the task should be run immediately. Defaults to false.\n\n#### `secure`\n\nIf `true`, the bot will use HTTPS. If `false`, it will use HTTP. Default value is `true`.\n\n#### `dbFile`\n\nThe bot tracks which items it has handled already in a SQLite database. Accepts a string path to the file to use a database: will create the file if it does not already exist.\nIf this option is not specified, the bot will store the SQLite DB in memory. This can be useful during development, but it is recommended to use a file when running in production.\n\n**Note**: If your bot doesn't need to keep track of events it has handled (e.g. it is posting new content to a community using a cronjob), you can omit optional dependencies to opt out of its use by installing the dependencies using `npm install --omit=optional` or your preferred package manager's equivalent.\n\n#### `markAsBot`\n\nIf true, the bot will automatically mark its own account as a bot. If false, make sure to remember to mark the bot's account\nas a bot in the account's settings.\n\nDefault value is `true`.\n\n#### `dryRun`\n\nIf `true`, no network requests will be made to your lemmy instance. If `false`, the bot will contact the instance configured in `instance`. Use this in combination with `enableLogs` and without `credentials` when doing development or testing to avoid unwanted actions in production.\n\nDefault value is `false`.\n\n#### `enableLogs`\n\nIf true, the bot will log every action it does to the console.\n\nDefault value is `true`.\n\n---\n\n### Bot Actions\n\nWhen handling an item or running a scheduled task, the bot will have access to several actions it can perform as an argument.\n\nThe actions are as follows, grouped by access level in ascending order:\n\n#### No login required\n\n- `getCommunity(form: GetCommunity)`: Retrieves community. the request form can accept either a community ID or a community name. If you are doing the latter, use `\"{community name}@{instance}\"` to get results more reliably.\n- `getPersonDetails(form: GetPersonDetails)`: Gets details about a person, including posts and comments they made and communities the moderate.\n- `getPost(form: GetPost)`: Retrieve a post based on its ID.\n- `getComment(form: GetComment)`: Retrieve a comment based on its ID.\n- `getParentOfComment(form: Comment)`: Retrieves the parent of a comment. Accepts a comment object, which is returned from handlers that deal with comments (e.g. comment handler, mention handler, reply handler, etc.). Returns an object with the following properties:\n  - `type` `\"post\"` or `\"comment\"`\n    When `type` is `\"post\"`:\n  - `post`: `GetPostResponse`\n    When `type` is `\"comment\"`:\n  - `comment`: `CommentResponse`\n- `isCommunityMod(form: {community: Community, person: Person})`: Returns whether or not a person is a moderator of a given community.\n\n#### Regular account\n\n- `createComment(form: CreateComment)`: Create a comment. Accepts an object with the following properties:\n  - `content` string\n  - `post_id` number\n  - `parent_id` _optional_ number\n  - `language_id` _optional_ number\n- `reportComment(form: ReportComment)`: Report a comment. Accepts an object with the following properties:\n  - `comment_id` number\n  - `reason` string\n- `reportPost(form: ReportPost)`: Report a post. Accepts an object with the following properties:\n  - `post_id` number\n  - `reason` string\n- `votePost(form: VotePost)`: Vote on a post. Accepts an object with the following properties:\n  - `post_id` number\n  - `vote` Vote\n- `voteComment(form: VoteComment)`: Vote on a comment. Accepts an object with the following properties:\n  - `comment_id` number\n  - `vote` Vote\n- `createPost(form: CreatePost)`: Create a post. `form` has the following properties:\n  - `name` string\n  - `url` _optional_ string\n  - `body` _optional_ string\n  - `nsfw` _optional_ boolean\n  - `language_id` _optional_ number\n  - `community_id` number\n  - `honeypot` _optional_ string\n- `sendPrivateMessage(form: SendPrivateMessage)`: Send a private message to a user. Accepts an object with the following properties:\n  - `recipient_id` number\n  - `content` string\n- `reportPrivateMessage(form: ReportPrivateMessage)`: Report a private message. Accepts an object with the following properties:\n  - `privateMessage_id` number\n  - `reason` string\n- `uploadImage(image: Buffer)`: Upload an image to pictrs. Returns a promise with an `UploadImageResponse`.\n- `resolveObject(form: ResolveObject)`: Resolves an object on a remote instance. Use this to federate with communities that aren't showing up on your local instance yet. **Note**: If passing in a string, make sure it is in the format \"!'community name'@'instance domain'\".\n- `followCommunity(form: FollowCommunity)`: Subscribe to a community.\n- `editPost(form: EditPost)`: Edit a post that was made previously by the bot. The `form` argument has the following properties:\n  - `post_id` number\n  - `name` _optional_ string\n  - `url` _optional_ string\n  - `body` _optional_ string\n  - `nsfw` _optional_ boolean\n  - `language_id` _optional_ number\n- `editComment(form: EditComment)`: Edit a comment previously made by the bot. The `form` argument has the following properties:\n  - `comment_id` number\n  - `content` _optional_ string\n  - `language_id` _optional_ number\n- `listMedia(form: ListMedia)`: List all media that was posted by the bot. The `form` argument is optional and has the following properties:\n  - `page`: number\n  - `limit`: number\n- `hidePost(form: HidePost)`: Hide posta so they don't show up in your feed. The `form` argument has the following properties:\n  - `postIds`: number[]\n  - `hide`: boolean\n\n#### Community moderator\n\n- `banFromCommunity(form: BanFromCommunity)`: Ban or unban a user from a community. Accepts an object with the following properties:\n  - `community_id` number\n  - `person_id` number\n  - `ban` boolean\n  - `expires` _optional_ number\n  - `reason` _optional_ string\n  - `remove_data` _optional_ boolean\n- `removePost(form: RemovePost)`: Remove a post. Accepts an object with the following properties:\n  - `post_id` number\n  - `removed` boolean\n  - `reason` _optional_ string\n- `distinguishComment(form: DistinguishComment)`: Pin a comment to the top of a comment thread. Accepts an object with the following properties:\n  - `comment_id` number\n  - `distinguished` boolean\n- `removeComment(form: RemoveComment)`: Remove a comment. Accepts an object with the following properties:\n  - `comment_id` number\n  - `removed` boolean\n  - `reason` _optional_ string\n- `resolveCommentReport(form: ResolveCommentReport)`: Resolve a comment report. Accepts an object with the following properties:\n  - `report_id` number\n  - `resolved` boolean\n- `resolvePostReport(form: ResolvePostReport)`: Resolve a post report. Accepts an object with the following properties:\n  - `report_id` number\n  - `resolved` boolean\n- `resolveMessageReport(form: ResolvePrivateMessageReport)`: Resolve a private message report. Accepts an object with the following propertied:\n  - `report_id` number\n  - `resolved` boolean\n- `featurePost(form: FeaturePost)`: Feature a post. Accepts an object with the following properties:\n  - `post_id` number\n  - `feature_type`: PostFeatureType\n  - `featured`: boolean\n- `lockPost(postId: number, locked: boolean)`: Lock/unlock a post. Accepts an object with the following properties:\n  - `pst_id` number\n  - `locked` boolean\n\n#### Admin\n\n- `getCommentVotes(form: ListCommentLikes)`: Show who voted on a comment, and what their vote is. Accepts an object with the following properties:\n  - `comment_id` number\n  - `page` **optional** number\n  - `limit` **optional** number\n- `getPostVotes(form: ListPostLikes)`: Show who voted on a post, and what their vote is. Accepts an object with the following properties:\n  - `post_id` number\n  - `page` **optional** number\n  - `limit` **optional** number\n- `banFromSite(form: BanFromSite)`: Ban or unban a user from the instance. Accepts an object with the following properties:\n  - `person_id` number\n  - `ban` number\n  - `expires` _optional_ number\n  - `reason` _optional_ string\n  - `remove_data` _optional_ boolean\n- `approveRegistrationApplication(form: ApproveRegistrationApplication)`: Approve the creation of an account. Accepts an object with the following properties:\n  - `id` number\n  - `approve` boolean\n  - `deny_reason` **optional** string\n- `listAllMedia(form: ListMedia)`: List all media that has been posted on the instance. Optionally accepts an object with the following properties:\n  - `page`: number\n  - `limit`: number\n\n## HTTP Client\n\nIf you need to use the [lemmy client](https://github.com/LemmyNet/lemmy-js-client) directly, the `__httpClient__` property is available so you don't need add it to your project separately. For your convenience, you can also access this in paramaters for polled event handlers and scheduled tasks.\n\n## Running Your Bot\n\nThere are templates for docker and systemd in the templates folder to help you run your bot once you've made it.\n\n## Examples\n\n### Live Examples\n\n- [Translator bot](https://github.com/SleeplessOne1917/lemmy-translator-bot)\n- [OCR scanner bot](https://github.com/SleeplessOne1917/lemmy-ocr-bot)\n- [Piped link bot](https://github.com/TeamPiped/lemmy-piped-link-bot)\n- [Karma bot](https://github.com/programming-dot-dev/karma-bot)\n- [Link bot](https://github.com/PangoraWeb/link-bot)\n- [Basedcount automoderator](https://github.com/basedcount/lemmy-automoderator)\n\n### Congratulator Bot\n\nThis bot will comment a cringy congratulations whenever a post on certain communities receives a score or 25 or more.\nPosts are valid to be handled after 10 minutes, but if a post ist congratulated it will no longer be eligible to be processed\n(due to `preventReprocess` being called). Posts that it's polling will be sorted by hot, and the bot will only be able to check posts in the shrek or tv communities on instance.xyz or the fediverse, cringe, and cooking communities on fediplace.ml.\n\n```typescript\nimport LemmyBot from 'lemmy-bot';\n\nconst bot = new LemmyBot({\n  instance: 'instance.xyz',\n  credentials: {\n    username: 'CongratulatorBot',\n    password: 'password'\n  },\n  connection: {\n    minutesUntilReprocess: 10,\n    secondsBetweenPolls: 120\n  },\n  dbFile: 'db.sqlite3',\n  federation: {\n    allowList: [\n      {\n        instance: 'instance.xyz',\n        communities: ['shrek', 'tv']\n      },\n      {\n        instance: 'fediplace.ml',\n        communities: ['fediverse', 'cringe', 'cooking']\n      }\n    ]\n  },\n  handlers: {\n    post: {\n      sort: 'Hot',\n      handle: ({\n        postView: {\n          counts: { score },\n          post: { id }\n        },\n        botActions: { createComment },\n        preventReprocess\n      }) =\u003e {\n        if (score \u003e 25) {\n          createComment({\n            post_id: id,\n            content:\n              'WOW, 25+ score!?!?! Das a lot of score-arinos!!!!! Congratulations fedizen! :)'\n          });\n          preventReprocess();\n        }\n      }\n    }\n  }\n});\n\nbot.start();\n```\n\n### Cringe username rejector\n\nThis bot will reject registration applications of anyone with a cringy username. The bot must have admin privileges to work.\n\n```typescript\nimport LemmyBot from 'lemmy-bot';\n\nconst cringeNameRegex = /(^(x|X)+.+(x|X)+$)|69|420/;\n\nconst bot = new LemmyBot({\n  instance: 'instance.ml',\n  credentials: {\n    username: 'CringeRejector',\n    password: 'password'\n  },\n  handlers: {\n    registrationApplication: ({\n      applicationView: {\n        creator: { name },\n        registration_application: { id }\n      },\n      botActions: { approveRegistrationApplication }\n    }) =\u003e {\n      if (cringeNameRegex.test(name)) {\n        approveRegistrationApplication({\n          id,\n          deny_reason: 'No cringy usernames allowed',\n          approve: false\n        });\n      }\n    }\n  }\n});\n\nbot.start();\n```\n\n## Credits\n\nLogo made by Andy Cuccaro (@andycuccaro) under the CC-BY-SA 4.0 license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSleeplessOne1917%2Flemmy-bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSleeplessOne1917%2Flemmy-bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSleeplessOne1917%2Flemmy-bot/lists"}