meow
This commit is contained in:
commit
94ce88aa65
8 changed files with 253 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
**/**.gif
|
||||
**/**.jpeg
|
||||
**/**.jpg
|
||||
**/**.mp4
|
||||
**/**.png
|
||||
.venv/
|
||||
auth/*.secret
|
||||
config/*.json
|
29
README.md
Normal file
29
README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Fedi Board Bot
|
||||
|
||||
A bot that posts random images from image boards to Mastodon, Iceshrimp, Sharkey or Akkoma.
|
||||
|
||||
## Config
|
||||
|
||||
Copy `config/example.json.example` and remove the `.example` from the end. All fields are explained in the file. Config names should not have any spaces.
|
||||
|
||||
When config-name is mentioned later do not include the `.json` extension.
|
||||
|
||||
## Usage
|
||||
|
||||
Install the requirements. You should ideally do this in a venv.
|
||||
|
||||
```sh
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
For every new config generate a login token by running
|
||||
|
||||
```sh
|
||||
python3 login.py <config-name> <application-name> <instance-url>
|
||||
```
|
||||
|
||||
Run the bot by running
|
||||
|
||||
```sh
|
||||
python3 bot.py <config-name>
|
||||
```
|
0
auth/.gitkeep
Normal file
0
auth/.gitkeep
Normal file
173
bot.py
Normal file
173
bot.py
Normal file
|
@ -0,0 +1,173 @@
|
|||
from dataclasses import dataclass
|
||||
from mastodon import Mastodon
|
||||
from os import environ
|
||||
from sys import argv
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import random
|
||||
import requests
|
||||
|
||||
class Config:
|
||||
block_tags: list[str]
|
||||
nsfw_tags: list[str]
|
||||
search_tags: list[str]
|
||||
text_cw: str
|
||||
text_filler: list[str]
|
||||
type: str
|
||||
|
||||
def __init__(self, config_name: str):
|
||||
with open(f"config/{config_name}.json") as file:
|
||||
config = json.load(file)
|
||||
|
||||
self.block_tags = config["blockTags"]
|
||||
self.nsfw_tags = config["nsfwTags"]
|
||||
self.search_tags = config["searchTags"]
|
||||
self.text_cw = config["textCW"]
|
||||
self.text_filler = config["textFiller"]
|
||||
self.type = config["type"]
|
||||
|
||||
def random_image_danbooru(block_tags: list[str], search_tags: list[str]) -> dict | None:
|
||||
tags = []
|
||||
for tag in block_tags:
|
||||
tags.append("-" + tag.strip())
|
||||
for tag in search_tags:
|
||||
tags.append(tag.strip())
|
||||
tags = " ".join(tags)
|
||||
|
||||
params = {
|
||||
"random": "1",
|
||||
"tags": tags
|
||||
}
|
||||
if environ.get("BOARD_API_KEY") and environ.get("BOARD_USERNAME"):
|
||||
params["api_key"] = environ.get("BOARD_API_KEY")
|
||||
params["login"] = environ.get("BOARD_USERNAME")
|
||||
resp = requests.get("https://danbooru.donmai.us/posts.json", params=params)
|
||||
|
||||
if resp.ok == False:
|
||||
print(resp.json())
|
||||
return None
|
||||
elif resp.json() == []:
|
||||
return None
|
||||
|
||||
return random.choice(resp.json())
|
||||
|
||||
def random_image_gelbooru(block_tags: list[str], search_tags: list[str]) -> dict | None:
|
||||
tags = ["sort:random"]
|
||||
for tag in block_tags:
|
||||
tags.append("-" + tag.strip())
|
||||
for tag in search_tags:
|
||||
tags.append(tag.strip())
|
||||
tags = " ".join(tags)
|
||||
|
||||
params = {
|
||||
"page": "dapi",
|
||||
"s": "post",
|
||||
"q": "index",
|
||||
"pid": 0,
|
||||
"json": "1",
|
||||
"limit": "1",
|
||||
"tags": tags
|
||||
}
|
||||
resp = requests.get("https://gelbooru.com/index.php", params=params)
|
||||
|
||||
if resp.ok == False:
|
||||
print(resp.json())
|
||||
return None
|
||||
elif resp.json()["post"] == []:
|
||||
return None
|
||||
|
||||
return random.choice(resp.json()["post"])
|
||||
|
||||
# Exit if config isn't specified
|
||||
if len(argv) < 2:
|
||||
print(f"Usage: python3 {argv[0]} <config-name>")
|
||||
exit()
|
||||
|
||||
config = Config(argv[1])
|
||||
|
||||
match config.type:
|
||||
case "danbooru":
|
||||
post = random_image_danbooru(config.block_tags, config.search_tags)
|
||||
case "gelbooru":
|
||||
post = random_image_gelbooru(config.block_tags, config.search_tags)
|
||||
case _:
|
||||
print("'type' in config must be 'danbooru' or 'gelbooru'")
|
||||
exit()
|
||||
|
||||
if post == None:
|
||||
print("Couldn't get an image")
|
||||
exit()
|
||||
|
||||
match config.type:
|
||||
case "danbooru":
|
||||
filename = f'{post["md5"]}.{post["file_ext"]}'
|
||||
case "gelbooru":
|
||||
filename = post["image"]
|
||||
case _:
|
||||
filename = "img.png"
|
||||
|
||||
print(post)
|
||||
|
||||
image = requests.get(post["file_url"])
|
||||
|
||||
if image.ok == False:
|
||||
print("Couldn't download image")
|
||||
exit()
|
||||
|
||||
with open(filename, "wb") as f:
|
||||
f.write(image.content)
|
||||
|
||||
# Image is NSFW if rated Explicit, Sensitive or Unknown
|
||||
is_nsfw = post["rating"].startswith("e") or post["rating"].startswith("s") or post["rating"] == None
|
||||
|
||||
if is_nsfw == False:
|
||||
for tag in config.nsfw_tags:
|
||||
# Check if any config defined NSFW tags are present
|
||||
match config.type:
|
||||
case "danbooru":
|
||||
if tag in post["tag_string"] or tag in post["tag_string_general"] or tag in post["tag_string_artist"] or tag in post["tag_string_copyright"] or tag in post["tag_string_character"] or tag in post["tag_string_meta"]:
|
||||
is_nsfw = True
|
||||
break
|
||||
case "gelbooru":
|
||||
if tag in post["tags"]:
|
||||
is_nsfw = True
|
||||
break
|
||||
case _:
|
||||
is_nsfw = True
|
||||
break
|
||||
|
||||
# This sucks but it works :3
|
||||
post_content = [
|
||||
random.choice(config.text_filler),
|
||||
"\n\n",
|
||||
"Artist: " + post["tag_string_artist"] + "\n" if config.type == "danbooru" and len(post["tag_string_artist"]) > 0 else "",
|
||||
"Score: " + str(post["score"]) + "\n",
|
||||
"Source: " + post["source"] + "\n\n" if len(post["source"]) > 0 else "\n",
|
||||
]
|
||||
|
||||
mastodon = Mastodon(access_token=f"auth/{argv[1]}.auth.secret")
|
||||
|
||||
match config.type:
|
||||
case "danbooru":
|
||||
tags = post["tag_string"]
|
||||
case "gelbooru":
|
||||
tags = post["tags"]
|
||||
case _:
|
||||
tags = "unknown"
|
||||
|
||||
media = mastodon.media_post(
|
||||
description="Automatically posted image. Tagged: " + tags,
|
||||
media_file=filename
|
||||
)
|
||||
|
||||
status = mastodon.status_post(
|
||||
"".join(post_content),
|
||||
media_ids=[media["id"]],
|
||||
sensitive=is_nsfw,
|
||||
spoiler_text="(NSFW) " + config.text_cw if is_nsfw else config.text_cw,
|
||||
visibility="unlisted"
|
||||
)
|
||||
print(status["url"])
|
||||
|
||||
pathlib.Path(filename).unlink()
|
16
config/example.json.example
Normal file
16
config/example.json.example
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"blockTags": [
|
||||
"List of tags that should be ignored by the bot."
|
||||
],
|
||||
"nsfwTags": [
|
||||
"If these tags are present the post will be marked as sensitive"
|
||||
],
|
||||
"searchTags": [
|
||||
"Tags that will be searched for"
|
||||
],
|
||||
"textCW": "All posts will have this content warning",
|
||||
"textFiller": [
|
||||
"Flavor text for posts"
|
||||
],
|
||||
"type": "danbooru or gelbooru"
|
||||
}
|
11
login.py
Normal file
11
login.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from mastodon import Mastodon
|
||||
from sys import argv
|
||||
|
||||
if len(argv) < 4:
|
||||
print(f"Usage: python3 {argv[0]} <config-name> <application-name> <instance-url>")
|
||||
exit()
|
||||
|
||||
Mastodon.create_app(argv[2], scopes=["write:media", "write:statuses"], to_file=f"auth/{argv[1]}.client.secret", api_base_url=argv[3])
|
||||
mastodon = Mastodon(client_id=f"auth/{argv[1]}.client.secret", api_base_url=argv[3])
|
||||
print(mastodon.auth_request_url(scopes=["write:media", "write:statuses"]))
|
||||
mastodon.log_in(code=input("Code="), scopes=["write:media", "write:statuses"], to_file=f"auth/{argv[1]}.auth.secret")
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Mastodon.py
|
15
shell.nix
Normal file
15
shell.nix
Normal file
|
@ -0,0 +1,15 @@
|
|||
{ pkgs ? import <nixpkgs> { } }:
|
||||
pkgs.mkShell {
|
||||
packages = [
|
||||
(pkgs.python3.withPackages (python-packages: [
|
||||
python-packages.black
|
||||
python-packages.pip
|
||||
python-packages.virtualenv
|
||||
]))
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
'';
|
||||
}
|
Loading…
Add table
Reference in a new issue