diff --git a/main.py b/main.py new file mode 100644 index 0000000000000000000000000000000000000000..f6f31c8a2c9495ce8d3191716754e92464d0b19e --- /dev/null +++ b/main.py @@ -0,0 +1,69 @@ +# Proof-of-concept: KI-Check von Bluesky-Konten +# +# 12-2024 Jan Eggers + +from src.check_bsky import * + +# Konstante +d_thresh = .8 # 80 Prozent +limit = 25 # Posts für den Check + +if __name__ == "__main__": + # Bluesky-Check + handle_str = input("Erstes Handle mit diesen Zeichen wählen: ") + handle = find_handles(handle_str)[0] + print(handle) + # Diese Funktion holt die Infos zum Profil 'handle': + # Erwartet einen String, gibt ein dict zurück. + # Beschreibung: https://docs.bsky.app/docs/api/app-bsky-actor-get-profile + # Manchmal existieren Felder wie 'description' nicht. + profile = call_get_profile(handle) + author = profile['did'] + print(author) + # Diese Funktion holt die Posts. + # 'author' darf irgendwas sein: handle, did... wir nehmen die did + # Gibt ein dict zurück: im Schlüssel 'feed' sind die einzelnen Posts gespeichert, + # im Key 'cursor' gibt es das Datum des frühesten abgefragten Posts zurück (es sei denn, + # es sind weniger Posts als limit, dann ist cursor leer.) + # Beschreibung: https://docs.bsky.app/docs/api/app-bsky-feed-get-author-feed + posts = call_get_author_feed(author, limit = limit) + # In diesem Demo-Programm werden die Posts hier noch nicht ausgewertet. + # Das passiert in der Extra-Funktion check_handle unten. + print(posts['cursor']) + # Funktion prüft die letzten ```limit``` Posts (voreingestellt auf 20) + # Erwartet ein Handle oder ein did - wir nehmen DID + # Gibt ein Dataframe zurück; Struktur ist oben in der Funktion beschrieben. + # Wichtigster Punkt: Ergebnis des KI-Checks in den Spalten + # - 'detectora_ai_score': Detectora-Score des Post-Textes (als real) + # - 'aiornot_ai_score': + df = check_handle(author, limit = limit) + n_posts = len(df) + print(f'\n\nAnalyse des Kontos @{handle} ({profile['displayName']}) seit {profile['createdAt']}- {profile['followersCount']} Follower') + print(f'{profile.get('description','---')}\n') + print(f'Anzahl der analysierten Posts: {n_posts}') + print(f"Durchschnittliche KI-Text-Wahrscheinlichkeit: {df['detectora_ai_score'].mean()}") + detectora_posts_df = df[df['detectora_ai_score'] >= d_thresh] + print(f"Anzahl von Posts über einer detectora-Schwelle von {d_thresh*100:.1f}%: {len(detectora_posts_df)}") + image_posts = [post for post in df['aiornot_ai_score'].to_list() if post is not None] + # Liste auspacken, nur die Dicts ohne None-Elemente + image_list = [item for sublist in image_posts for item in sublist] + ai_list = [item for item in image_list if item['aiornot_score']!='human'] + if len(image_list) == 0: + p_ai = 0 + else: + p_ai = len(ai_list)/len(image_list) * 100 + print(f"Anzahl der Bilder: {len(image_list)}, verdächtig: {len(ai_list)} ({p_ai:.1f})%") + # Jetzt die Daten abspeichern + # Fals das Directory nicht existiert, anlegen + if not os.path.exists('bsky-checks'): + os.makedirs('bsky-checks') + + # Read existing file if it exists + filename = f'bsky-checks/{handle}.csv' + if os.path.exists(filename): + existing_df = pd.read_csv(filename) + df = pd.concat([existing_df, df]).drop_duplicates(subset=['uri']).reset_index(drop=True) + + df.to_csv(f'bsky-checks/{handle}.csv', index=False) # Save to CSV for example + + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..bcc95daae98361532eb702432bac580f6b423075 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "aichecker" +version = "0.1.0" +description = "Bluesky-Konten auf KI-Inhalte checken" +requires-python = ">=3.8" +dependencies = [ + "requests", # Alle bsky-Calls über Requests + "openai", # für die Bildbeschreibung + "ollama", # für die Bildbeschreibung +] + +[project.optional-dependencies] +dev = [ + "pytest", +] + +[tool.hatch.build.targets.wheel] +packages = ["src"] \ No newline at end of file diff --git a/__init__.py b/src/__init__.py similarity index 100% rename from __init__.py rename to src/__init__.py diff --git a/bildbeschreibung.py b/src/bildbeschreibung.py similarity index 98% rename from bildbeschreibung.py rename to src/bildbeschreibung.py index 548c1c2941504ef1827139d9cb1112189c05dbc2..924fb1836fd5e58bec647d2b86be8ba2e55a4b50 100644 --- a/bildbeschreibung.py +++ b/src/bildbeschreibung.py @@ -1,8 +1,5 @@ -import re -import locale import ollama from openai import OpenAI -import whisper from pathlib import Path import os import base64 diff --git a/check_bsky_profile.py b/src/check_bsky.py similarity index 98% rename from check_bsky_profile.py rename to src/check_bsky.py index 266baf48d29e7e5118678e9486b515ab6f52149c..77a0b1486e39e624d1d63cfba6986d567505ba27 100644 --- a/check_bsky_profile.py +++ b/src/check_bsky.py @@ -5,9 +5,9 @@ import json import pandas as pd -from detectora import query_detectora -from imagecheck import query_aiornot -from bildbeschreibung import gpt4_description +from .detectora import query_detectora +from .imagecheck import query_aiornot +from .bildbeschreibung import gpt4_description import requests import os diff --git a/detectora.py b/src/detectora.py similarity index 100% rename from detectora.py rename to src/detectora.py diff --git a/imagecheck.py b/src/imagecheck.py similarity index 97% rename from imagecheck.py rename to src/imagecheck.py index e6c2e8506389e59f1df8acb5aabf0b8fbe47a756..6ebc7336bd8fd4f99e4c797690995ccf2d9ef258 100644 --- a/imagecheck.py +++ b/src/imagecheck.py @@ -1,6 +1,6 @@ # imagecheck.py # Erfragt KI-Wahrscheinlichkeit für ein Bild über Hive- und AIorNot-API -from bildbeschreibung import ai_description +from src.bildbeschreibung import ai_description import requests import json