Skip to content
Snippets Groups Projects
Commit aea6b897 authored by Jan Eggers's avatar Jan Eggers
Browse files

main_tg gefixt

parent b71e39e4
No related branches found
No related tags found
No related merge requests found
No preview for this file type
...@@ -94,17 +94,22 @@ if __name__ == "__main__": ...@@ -94,17 +94,22 @@ if __name__ == "__main__":
filename = f'tg-checks/{handle}.csv' filename = f'tg-checks/{handle}.csv'
if os.path.exists(filename): if os.path.exists(filename):
existing_df = reimport_csv(filename) existing_df = reimport_csv(filename)
max_nr = max(existing_df['nr']) start_post = max(existing_df['nr'])
print(f"Dieser Kanal wurde schon einmal ausgelesen, zuletzt Post Nr.: {max_nr} - seitdem {last_post-max_nr} neue Posts") print(f"Dieser Kanal wurde schon einmal ausgelesen, zuletzt Post Nr.: {start_post} - seitdem {last_post-start_post} neue Posts")
else: else:
max_nr = last_post-N start_post = last_post-N+1
print(f"Noch nicht gespeichert. Importiere {N} Posts bis zum letzten: {last_post}.")
# Lies die aktuellsten Posts, sichere und analysiere sie # Lies die aktuellsten Posts, sichere und analysiere sie
# #
print("Einlesen/mit KI beschreiben: ", end="") print("Einlesen: ", end="")
posts = tgc_read_range(handle_str, max_nr+1, last_post) posts = tgc_read_range(handle_str, start_post, last_post, save=False, describe= False)
print() # für die Fortschrittsmeldung print() # für die Fortschrittsmeldung
print("Inhalte sichern und mit KI beschreiben: ", end="")
hydrated_posts = tg_hydrate(posts)
print()
print("Auf KI-Inhalt prüfen: ",end="") print("Auf KI-Inhalt prüfen: ",end="")
checked_posts = check_tg_list(posts, check_images = True) # Bearbeitet nur die Posts, für die Inhalte hinterlegt sind
checked_posts = tg_evaluate(hydrated_posts)
# #
n_images = 0 n_images = 0
n_ai_images = 0 n_ai_images = 0
...@@ -118,16 +123,20 @@ if __name__ == "__main__": ...@@ -118,16 +123,20 @@ if __name__ == "__main__":
# Detectora-Score für diesen Text abrufen; wenn über der Schwelle, # Detectora-Score für diesen Text abrufen; wenn über der Schwelle,
# KI-Texte um eins hochzählen # KI-Texte um eins hochzählen
n_ai_texts += 1 if post.get('detectora_ai_score',0) > DETECTORA_T else 0 n_ai_texts += 1 if post.get('detectora_ai_score',0) > DETECTORA_T else 0
if post['photo'] is not None:
n_images += 1
ai_score = post.get('aiornot_ai_score',{'confidence': 0})['confidence']
n_ai_images += 1 if ai_score > AIORNOT_T else 0
if post['video'] is not None: if post['video'] is not None:
n_videos += 1 n_videos += 1
ai_score = post['aiornot_ai_score'].get('confidence', 0) ai_score = post['aiornot_ai_score'].get('confidence', 0)
n_ai_videos += 1 if ai_score > AIORNOT_T else 0 n_ai_videos += 1 if ai_score > AIORNOT_T else 0
elif post['photo'] is not None:
n_images += 1
ai_score = post.get('aiornot_ai_score')
if ai_score is None:
ai_score = 0
else:
ai_score = ai_score['confidence']
n_ai_images += 1 if ai_score > AIORNOT_T else 0
print(f"In den {N} Posts: ") print(f"\n\nIn den {N} Posts: ")
print(f" - Texte: {n_texts}, davon KI-verdächtig: {n_ai_texts} (Schwelle: {DETECTORA_T})") print(f" - Texte: {n_texts}, davon KI-verdächtig: {n_ai_texts} (Schwelle: {DETECTORA_T})")
print(f" - Bilder: {n_images}, davon KI-verdächtig: {n_ai_images} (Schwelle: {AIORNOT_T})") print(f" - Bilder: {n_images}, davon KI-verdächtig: {n_ai_images} (Schwelle: {AIORNOT_T})")
print(f"Ergebnis wird in 'tg-checks/{handle}.csv' mit abgespeichert. ") print(f"Ergebnis wird in 'tg-checks/{handle}.csv' mit abgespeichert. ")
......
...@@ -10,7 +10,7 @@ authors = [ ...@@ -10,7 +10,7 @@ authors = [
maintainers = [ maintainers = [
{name = "Jan Eggers", email = "jan.eggers@hr.de"}, {name = "Jan Eggers", email = "jan.eggers@hr.de"},
] ]
version = "0.2.4.1" # Neue Versionsnummern für pip-Update version = "0.2.4.2" # Neue Versionsnummern für pip-Update
description = "Bluesky- und Telegram-Konten auf KI-Inhalte checken" description = "Bluesky- und Telegram-Konten auf KI-Inhalte checken"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = [ dependencies = [
......
...@@ -3,4 +3,4 @@ from .transcribe import ai_description, convert_mp4_to_mp3, convert_ogg_to_m4a, ...@@ -3,4 +3,4 @@ from .transcribe import ai_description, convert_mp4_to_mp3, convert_ogg_to_m4a,
from .detectora import query_detectora from .detectora import query_detectora
from .aiornot import query_aiornot from .aiornot import query_aiornot
from .check_wrappers import aiornot_wrapper, detectora_wrapper, bsky_aiornot_wrapper from .check_wrappers import aiornot_wrapper, detectora_wrapper, bsky_aiornot_wrapper
from .check_tg import tgc_clean, tgc_read, tgc_blockread, tgc_read_url, tgc_profile, tgc_read_range, tgc_read_number, check_tg_list from .check_tg import tgc_clean, tgc_read, tgc_blockread, tgc_read_url, tgc_profile, tgc_read_range, tgc_read_number, tg_evaluate, tg_hydrate
\ No newline at end of file \ No newline at end of file
...@@ -360,7 +360,7 @@ def tgc_blockread(cname="telegram", nr=None, save=True, describe=False): ...@@ -360,7 +360,7 @@ def tgc_blockread(cname="telegram", nr=None, save=True, describe=False):
return posts return posts
def tgc_read_range(cname, n1=1, n2=None, save=True, describe = True): def tgc_read_range(cname, n1=1, n2=None, save=True, describe = True):
# Liest einen Bereich von Posts # Liest einen Bereich von Post n1 bis Post n2
# Zuerst: Nummer des letzten Posts holen # Zuerst: Nummer des letzten Posts holen
profile = tgc_profile(cname) profile = tgc_profile(cname)
# Sicherheitscheck: erste Post-Nummer überhaupt schon gepostet? # Sicherheitscheck: erste Post-Nummer überhaupt schon gepostet?
...@@ -372,15 +372,15 @@ def tgc_read_range(cname, n1=1, n2=None, save=True, describe = True): ...@@ -372,15 +372,15 @@ def tgc_read_range(cname, n1=1, n2=None, save=True, describe = True):
n2 = max_nr n2 = max_nr
posts = [] posts = []
while n <= n2: while n <= n2:
max = n max = n2
new_posts = tgc_blockread(cname, n, save, describe) new_posts = tgc_blockread(cname, n, save, describe)
for p in new_posts: for p in new_posts:
if p['nr'] > n2: if p['nr'] > n2:
return posts return posts
if p['nr'] >= n: if p['nr'] >= n:
posts.append(p) posts.append(p)
if p['nr'] > max: if p['nr'] == n2:
max = p['nr'] return posts
n = max n = max
return posts return posts
...@@ -418,19 +418,15 @@ def tgc_read_number(cname, n = 20, cutoff = None, save=True, describe = True): ...@@ -418,19 +418,15 @@ def tgc_read_number(cname, n = 20, cutoff = None, save=True, describe = True):
# Routine checkt eine Post-Liste, wie sie aus den tgc_read... Routinen kommen. # Routine checkt eine Post-Liste, wie sie aus den tgc_read... Routinen kommen.
# Wenn noch kein KI-Check vorliegt, wird er ergänzt. # Wenn noch kein KI-Check vorliegt, wird er ergänzt.
# Setzt allerdings voraus, dass die entsprechenden Inhalte schon abgespeichert sind. # Setzt allerdings voraus, dass die entsprechenden Inhalte schon abgespeichert sind.
def check_tg_list(posts, check_images = True):
posts = [p for p in posts if p is not None] def tg_evaluate(posts, check_texts = True, check_images = True):
# Nimmt eine Liste von Posts und ergänzt KI-Einschätzung von Detectora
# und AIORNOT.
for post in posts: for post in posts:
if 'detectora_ai_score' not in post: if ('detectora_ai_score' not in post) and check_texts:
# Noch keine KI-Einschätzung für den Text? # Noch keine KI-Einschätzung für den Text?
post['detectora_ai_score'] = detectora_wrapper(post['text']) post['detectora_ai_score'] = detectora_wrapper(post['text'])
# Leerzeile für den Fortschrittsbalken if ('aiornot_ai_score' not in post) and check_images:
print()
if not check_images:
return
# Okay, es geht weiter: Bilder auf KI prüfen
for post in posts:
if 'aiornot_ai_score' not in post:
if post['video'] is not None: if post['video'] is not None:
# Audio des Videos analysieren # Audio des Videos analysieren
fname = post['video'].get('file') fname = post['video'].get('file')
...@@ -442,22 +438,35 @@ def check_tg_list(posts, check_images = True): ...@@ -442,22 +438,35 @@ def check_tg_list(posts, check_images = True):
fname = post['voice'].get('file') fname = post['voice'].get('file')
post['aiornot_ai_score'] = aiornot_wrapper(convert_ogg_to_mp3(fname), is_image = False) post['aiornot_ai_score'] = aiornot_wrapper(convert_ogg_to_mp3(fname), is_image = False)
return posts return posts
# Wrapper für die check_tg_list Routine.
# Gibt Resultate als df zurück, arbeitet aber hinter den Kulissen mit
# einer Liste von dicts (anders als check_bsky)
def tg_hydrate(posts): def tg_hydrate(posts):
# Nimmt eine Liste von Posts und zieht die zugehörigen Dateien, # Nimmt eine Liste von Posts und zieht die zugehörigen Dateien,
# erstellt Beschreibungen und Transkriptionen. # erstellt Beschreibungen und Transkriptionen.
# #
# Fernziel: Asynchrone Verarbeitung. # Fernziel: Asynchrone Verarbeitung.
return posts
def tg_evaluate(posts, check_texts = True, check_images = True):
# Nimmt eine Liste von Posts und ergänzt KI-Einschätzung von Detectora
# und AIORNOT.
for post in posts: for post in posts:
channel = post['channel']
b_nr = post['nr']
# Transcribe video and describe thumbnail
if post['video'] is not None and post['video'].get('file', None) is None:
# Save video to file
video_url = post['video'].get('url')
vfile = save_url(video_url, f"{channel}_{b_nr}_video")
post['video']['file'] = vfile
# Now transcribe video file
post['video']['transcription'] = transcribe(vfile)
# Fun fact: video also saves a thumbnail for good measure
if post['photo'] is not None and post['photo'].get('file', None) is None:
photo_url = post['photo']['url']
pfile = save_url(photo_url, f"{channel}_{b_nr}_photo")
post['photo']['file'] = pfile
image = base64.b64encode(requests.get(photo_url).content).decode('utf-8')
post['photo']['description'] = gpt4_description(f"data:image/jpeg;base64, {image}")
if post['voice'] is not None and post['voice'].get('file', None) is None:
voice_url = post['voice']['url']
vfile = save_url(voice_url, f"{channel}_{b_nr}_voice")
post['voice']['file'] = vfile
post['voice']['transcription'] = gpt4_description(vfile)
return posts return posts
def retrieve_tg_csv(cname, path= "tg-checks"): def retrieve_tg_csv(cname, path= "tg-checks"):
......
...@@ -8,7 +8,7 @@ import logging ...@@ -8,7 +8,7 @@ import logging
# Installieren mit # Installieren mit
# pip install aiornot # pip install aiornot
from aiornot import Client from aiornot import Client, AsyncClient
# Konstante # Konstante
d_thresh = .8 # 80 Prozent d_thresh = .8 # 80 Prozent
...@@ -81,6 +81,41 @@ def aiornot_wrapper(content, is_image = True): ...@@ -81,6 +81,41 @@ def aiornot_wrapper(content, is_image = True):
print("\b,") print("\b,")
return None return None
async def async_aiornot_wrapper(content, is_image = True):
# Create a client (reads AIORNOT_API_KEY env)
async_client = AsyncClient()
# Check if the API is up
if not await async_client.is_live():
logging.error("AIORNOT API nicht erreichbar")
exit(1)
# Check your token
resp = await async_client.check_token()
if not resp.is_valid:
logging.error("AIORNOT-API-Token nicht gültig")
exit(1)
is_url = (content.startswith("http://") or content.startswith("https://"))
if is_image:
if is_url:
response = await async_client.image_report_by_url(content)
else:
response = await async_client.image_report_by_file(content)
else: # (Audio)
if is_url:
response = await async_client.audio_report_by_url(content)
else:
response = await async_client.audio_report_by_file(content)
if response is None:
return response
else:
aiornot_dict = ({
'score': response.report.verdict,
# Unterscheidung: Bilder haben den Confidence score im Unter-Key 'ai'
# Audios SOLLTEN eien Confidence-Wert in response.report.confidence haben, haben es aber nicht
'confidence': response.report.ai.confidence if hasattr(response.report, 'ai') else .99,
'generator': object_to_dict(response.report.generator) if hasattr(response.report, 'generator') else None,
})
print(f"\b{'X' if aiornot_dict['score'] != 'human' else '.'}",end="")
return aiornot_dict
def bsky_aiornot_wrapper(did,embed): def bsky_aiornot_wrapper(did,embed):
# Verpackung für die AIORNOT-Funktion: # Verpackung für die AIORNOT-Funktion:
......
...@@ -19,7 +19,7 @@ Du erstellst eine deutsche Bildbeschreibung für den Alt-Text. ...@@ -19,7 +19,7 @@ Du erstellst eine deutsche Bildbeschreibung für den Alt-Text.
Beschreibe, was auf dem Bild zu sehen ist. Beschreibe, was auf dem Bild zu sehen ist.
Beginne sofort mit der Beschreibung. Sei präzise und knapp. Beginne sofort mit der Beschreibung. Sei präzise und knapp.
Wenn das Bild lesbaren Text enthält, zitiere diesen Text.""" Wenn das Bild lesbaren Text enthält, zitiere diesen Text."""
client = OpenAI(api_key = os.environ.get('OPENAI_API_KEY'))
# Use GPT-4 mini to describe images # Use GPT-4 mini to describe images
OLLAMA = False OLLAMA = False
...@@ -27,6 +27,7 @@ def gpt4_description(image_url): ...@@ -27,6 +27,7 @@ def gpt4_description(image_url):
# Check a local image by converting it to b64: # Check a local image by converting it to b64:
# image_url = f"data:image/jpeg;base64, {b64_image}" # image_url = f"data:image/jpeg;base64, {b64_image}"
print(".", end="") print(".", end="")
client = OpenAI(api_key = os.environ.get('OPENAI_API_KEY'))
response = client.chat.completions.create( response = client.chat.completions.create(
model="gpt-4o-mini", model="gpt-4o-mini",
messages=[ messages=[
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment