diff options
author | RaindropsSys <contact@minteck.org> | 2023-07-28 18:49:57 +0200 |
---|---|---|
committer | RaindropsSys <contact@minteck.org> | 2023-07-28 18:49:57 +0200 |
commit | d95cd0875006a2e02676a3ba53b15a39ce01b91a (patch) | |
tree | df629c29f02fb1b89c03c506021bf297709b78ae | |
download | derp-d95cd0875006a2e02676a3ba53b15a39ce01b91a.tar.gz derp-d95cd0875006a2e02676a3ba53b15a39ce01b91a.tar.bz2 derp-d95cd0875006a2e02676a3ba53b15a39ce01b91a.zip |
Initial commit
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | .idea/.gitignore | 8 | ||||
-rw-r--r-- | .idea/deployment.xml | 15 | ||||
-rw-r--r-- | .idea/derp.iml | 12 | ||||
-rw-r--r-- | .idea/discord.xml | 7 | ||||
-rw-r--r-- | .idea/inspectionProfiles/Project_Default.xml | 12 | ||||
-rw-r--r-- | .idea/misc.xml | 4 | ||||
-rw-r--r-- | .idea/modules.xml | 8 | ||||
-rw-r--r-- | config/__pycache__/raindrops.cpython-310.pyc | bin | 0 -> 471 bytes | |||
-rw-r--r-- | config/raindrops.py | 33 | ||||
-rw-r--r-- | derp.sh | 3 | ||||
-rw-r--r-- | main.py | 135 |
12 files changed, 240 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24f4c4d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +auth.txt +token-*.txt +list-*.txt
\ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..c2acf39 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="PublishConfigData" autoUpload="Always" serverName="bridlewood" remoteFilesAllowedToDisappearOnAutoupload="false"> + <serverData> + <paths name="bridlewood"> + <serverdata> + <mappings> + <mapping deploy="/opt/derp" local="$PROJECT_DIR$" web="/" /> + </mappings> + </serverdata> + </paths> + </serverData> + <option name="myAutoUpload" value="ALWAYS" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/derp.iml b/.idea/derp.iml new file mode 100644 index 0000000..8f6a3ea --- /dev/null +++ b/.idea/derp.iml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="WEB_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <excludeFolder url="file://$MODULE_DIR$/.tmp" /> + <excludeFolder url="file://$MODULE_DIR$/temp" /> + <excludeFolder url="file://$MODULE_DIR$/tmp" /> + </content> + <orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..d8e9561 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="DiscordProjectSettings"> + <option name="show" value="PROJECT_FILES" /> + <option name="description" value="" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..34b1592 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="ignoredIdentifiers"> + <list> + <option value="config" /> + </list> + </option> + </inspection_tool> + </profile> +</component>
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dc9ea49 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" /> +</project>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3f23d1c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/derp.iml" filepath="$PROJECT_DIR$/.idea/derp.iml" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/config/__pycache__/raindrops.cpython-310.pyc b/config/__pycache__/raindrops.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..4cfaa76 --- /dev/null +++ b/config/__pycache__/raindrops.cpython-310.pyc diff --git a/config/raindrops.py b/config/raindrops.py new file mode 100644 index 0000000..53b62dc --- /dev/null +++ b/config/raindrops.py @@ -0,0 +1,33 @@ +# Derp configuration file + +# --------------------------- +# Paths to the data files for this configuration file +sent_list_path = "list-raindrops.txt" +token_path = "token-raindrops.txt" + +# --------------------------- +# The tags to show in the notification, considered high-importance tags + +triggers = {"explicit", "suggestive", "grimdark", "semi-grimdark", "grotesque", "questionable", "foalcon", "male", "intersex", "bondage", "close-up", "tentacles"} + +# --------------------------- +# The query to send to Derpibooru when looking for new images + +query = "my:watched" + +# --------------------------- +# If you want to use a Derpibooru filter when fetching images, enter its ID here + +filter_id = 206751 # 56027 is the "Everything" filter, meaning nothing is filtered + +# --------------------------- +# The size the images should be cropped at when they are attached to the notification. + +resolution = (1000, 500) # Recommended for Android 12+ +#resolution = (1300, 400) # Recommended for Android 9-11 + +# --------------------------- +# The user name associated with the Derpibooru account +# Notifications are sent to the 'derpibooru-<username>' channel + +user_name = "RaindropsSys"
\ No newline at end of file @@ -0,0 +1,3 @@ +#!/bin/bash + +python main.py
\ No newline at end of file @@ -0,0 +1,135 @@ +import requests +from PIL import Image, ImageFilter, ImageEnhance, ImageOps +from io import BytesIO + +import config.raindrops as config + +DERPI_ENDPOINT = "https://derpibooru.org/api/v1/json/search/images" +NOTIFY_ENDPOINT = f"https://notifications.equestria.dev/derpibooru-{config.user_name}" + + +def should_notify(target_post, sent_list): + return target_post['id'] not in sent_list and target_post['processed'] and not target_post["deletion_reason"] + + +def do_request(query, filter_id): + params = {"key": api_key, "q": query, "filter_id": filter_id, "per_page": 50} + + return requests.get(DERPI_ENDPOINT, params=params) + + +def get_artists(tags): + artists = filter(lambda tag: tag.startswith("artist:"), tags) + artists = map(lambda tag: tag.removeprefix("artist:"), artists) + artists = format_artists(list(artists)) + + return artists + + +def format_artists(artists): + if len(artists) == 1: + return artists[0] + + return ", ".join(artists[:-1]) + " and " + artists[-1] + + +def get_censored_tags(tags, trigger_tags): + overlap = tags.intersection(trigger_tags) + return overlap + + +def get_thumbnail(image_url, censor, resolution, target_post): + if not target_post['mime_type'].startswith("image/"): + return None + + image = Image.open(BytesIO(requests.get(image_url).content)) + image = image.convert("RGBA") + + thumb = image.resize(resolution) + thumb = thumb.filter(ImageFilter.GaussianBlur(80)) + thumb = ImageEnhance.Brightness(thumb).enhance(0.5) + + if censor: + image.filter(ImageFilter.GaussianBlur(40)) + + #image = ImageOps.pad(image, config.resolution, color=(0, 0, 0, 0)) + #thumb.paste(image, (0, 0), image) + + image = ImageOps.contain(image, resolution) + thumb.paste(image, ((resolution[0] - image.size[0]) // 2, 0)) + + buffer = BytesIO() + thumb.save(buffer, format="PNG") + + return buffer + + +def get_prefix(censored_tags): + if len(censored_tags) > 0: + prefix = "⚠️ " + ", ".join(censored_tags) + else: + prefix = "safe" + + return prefix + + +def get_description(target_post): + if not target_post["description"]: + return None + + return target_post["description"].replace("\r", " ").replace("\n", " ").strip() + + +def get_notify_data(target_post, thumb_res, trigger_tags): + post_id = target_post["id"] + width = target_post["width"] + height = target_post["height"] + tags = target_post["tags"] + censor_tags = get_censored_tags(set(tags), trigger_tags) + artists = get_artists(tags) + + prefix = get_prefix(censor_tags) + description = get_description(target_post) + + title = f"#{post_id} - by {artists}" + message = f"{prefix} - {width}x{height}" + censor = len(censor_tags) > 0 + + if description: + message += f" - {description}" + + click = f"https://derpibooru.org/images/{post_id}" + thumb = get_thumbnail(target_post['representations']['medium'], censor, thumb_res, target_post) + notify_data = {"title": title, "message": message, "click": click, "thumb": thumb} + + return notify_data + + +def send_notification(notify_data, auth): + requests.post(NOTIFY_ENDPOINT, data=notify_data["thumb"].getvalue() if notify_data["thumb"] else None, + headers={"Authorization": f"Basic {auth}", "User-Agent": "Mozilla/5.0 (+Derp/0.0)", + "Tags": "derpibooru", "Title": notify_data["title"].encode("utf-8"), + "Message": notify_data["message"].encode("utf-8"), + "Click": notify_data["click"].encode("utf-8"), + "Icon": "https://derpicdn.net/img/view/2020/7/23/2406370.png"}) + + +api_key = open(config.token_path, "r").read().strip() +ntfy_auth = open("auth.txt", "r").read().strip() + +response = do_request(config.query, config.filter_id if config.filter_id >= 0 else None) +data = response.json() + +with open(config.sent_list_path, "r+") as f: + sent = [int(image_id.strip()) for image_id in f.readlines()] + + for post in reversed(data['images']): + if not should_notify(post, sent): + continue + + post_notify_data = get_notify_data(post, config.resolution, config.triggers) + send_notification(post_notify_data, ntfy_auth) + + print(post['id']) + sent.append(post['id']) + f.write(f"{post['id']}\n") |