initial commit with first prototype
This commit is contained in:
commit
871c276342
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# 1. Use a lightweight Python image
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# 2. Environment settings
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV PIP_NO_CACHE_DIR=1
|
||||||
|
|
||||||
|
# 3. Set working directory inside the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 4. Copy and install dependencies first (better caching)
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 5. Copy all your app files into the container
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 6. Set the default command to run your script
|
||||||
|
ENTRYPOINT ["python", "job_suggestor.py"]
|
||||||
1
already_suggested_companies.txt
Normal file
1
already_suggested_companies.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Bereits vorgeschlagene Unternehmen
|
||||||
127
cv_sebastian_egli.txt
Normal file
127
cv_sebastian_egli.txt
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
Dr. SebastianEgli
|
||||||
|
|
||||||
|
Geoinformatiker
|
||||||
|
|
||||||
|
Person
|
||||||
|
Geb: 18.01.1986
|
||||||
|
Nat: Deutsch, Schweiz
|
||||||
|
|
||||||
|
Adresse
|
||||||
|
A. d. Schülerhecke 13
|
||||||
|
35037 Marburg
|
||||||
|
Deutschland
|
||||||
|
|
||||||
|
Tel & E-Mail
|
||||||
|
+49 174 2465052
|
||||||
|
seb.egli@gmail.com
|
||||||
|
|
||||||
|
Schwerpunkte
|
||||||
|
Business Intelligence
|
||||||
|
Datenanalyse
|
||||||
|
Erdbeobachtung (EO)
|
||||||
|
Maschinelles Lernen
|
||||||
|
Agrarmodellierung
|
||||||
|
Klimatologie
|
||||||
|
|
||||||
|
Werkzeuge
|
||||||
|
QGIS, GDAL, OGR
|
||||||
|
Pandas, Numpy
|
||||||
|
Xarray, Satpy
|
||||||
|
Scikit-learn
|
||||||
|
Tensorflow, Mxnet
|
||||||
|
Open Data Cube
|
||||||
|
STAC, ERPNext
|
||||||
|
airtable, metabase
|
||||||
|
n8n, Kobo, Python
|
||||||
|
Java, R, Kotlin
|
||||||
|
|
||||||
|
Sprachen
|
||||||
|
Deutsch
|
||||||
|
Englisch
|
||||||
|
Französisch
|
||||||
|
|
||||||
|
Berufserfahrung
|
||||||
|
01/14 - heute Mitgründer & Entwickler
|
||||||
|
Bridgesoft GbR, Marburg, Deutschland
|
||||||
|
Entwicklung mobiler Anwendungen für mittelständische Unternehmen. Part-
|
||||||
|
ner: PC Gärtner GmbH, Ökobox-Online, Bosshammersch Hof, Infralytic
|
||||||
|
GmbH
|
||||||
|
|
||||||
|
09/22 - 07/25 Chief Data Scientist
|
||||||
|
agriBORA GmbH, Deutschland/Kenia
|
||||||
|
Teamleitung, Projekt- und Produktkonzeption, Landwirtschaftliches Moni-
|
||||||
|
toring und Modellierung, satellitengestützte Feldflächenerkennung,
|
||||||
|
Ertragsprognose, Kreditbewertung mittels maschinellem Lernen
|
||||||
|
|
||||||
|
11/19 - 08/22 Wissenschaftlicher Mitarbeiter LCRS, Philipps-Universität Marburg, Deutschland
|
||||||
|
Atmosphärische Fernerkundung, Softwareentwicklung für Lehrzwecke in
|
||||||
|
der Atmosphärenwissenschaft, Entwicklung von Anwendungen für das
|
||||||
|
Waldmanagement mittels UAV-Einsatz, Maschinelles Lernen
|
||||||
|
|
||||||
|
06/15 - 05/19 Wissenschaftlicher Mitarbeiter LCRS, Philipps-Universität Marburg, Deutschland
|
||||||
|
Erkennung von Wolken und Nebel in geostationären Satellitendaten, Ent-
|
||||||
|
wicklung von Fernerkundungsalgorithmen, Integration von Anwendungen
|
||||||
|
des maschinellen Lernens
|
||||||
|
|
||||||
|
03/13 - 01/14 Wissenschaftlicher Mitarbeiter LCRS, Philipps-Universität Marburg, Deutschland
|
||||||
|
Untersuchung der vertikalen Verteilung mikrophysikalischer Eigenschaften
|
||||||
|
in Strahlungsnebeln mittels Radarprofilierung und ballongetragenen Profil-
|
||||||
|
messungen.
|
||||||
|
|
||||||
|
05/11 - 01/13 Studentische Hilfskraft
|
||||||
|
LCRS, Philipps-Universität Marburg, Deutschland
|
||||||
|
Satellitendatenakquise, Verwaltung operationeller Stationsdaten der Mar-
|
||||||
|
burger Ground Truth- und Profiling-Station, Entwicklung von Werkzeugen
|
||||||
|
zur automatischen Datenkonvertierung verschiedener atmosphärischer
|
||||||
|
Messinstrumente.
|
||||||
|
|
||||||
|
02/11 - 04/11 Praktikant
|
||||||
|
Deutscher Wetterdienst (DWD), Offenbach, Deutschland
|
||||||
|
Erfassung verfügbarer In-situ-Hageldaten beim DWD, Durchführbarkeits-
|
||||||
|
analyse einer Hagelklimatologie, Analyse räumlich-zeitlicher Variationen
|
||||||
|
der Hagelhäufigkeit und Erstellung von Hagelrisikokarten.
|
||||||
|
|
||||||
|
02/09 - 04/09 Praktikant
|
||||||
|
Bundesamt für Kartographie und Geodäsie (BKG), Frankfurt, Deutschland
|
||||||
|
Entwicklung eines digitalen Landschaftsmodells (Maßstab: 1:1.000.000)
|
||||||
|
auf Grundlage der beim BKG verwendeten digitalen topografischen Kar-
|
||||||
|
tenbasis.
|
||||||
|
|
||||||
|
Preise
|
||||||
|
10/20 Wissenschaftspreis 2019
|
||||||
|
Industrie- und Handelskammer Kassel-Marburg
|
||||||
|
Der Preis wird zur Würdigung herausragender Dissertationen verliehen, die
|
||||||
|
eine hohe Relevanz für wirtschaftliche Fragestellungen aufweisen.
|
||||||
|
Verliehen für die Dissertation: “Satellite-Based Fog Detection: A Dynamic
|
||||||
|
Retrieval Method for Europe Based on Machine Learning“
|
||||||
|
|
||||||
|
Ausbildung
|
||||||
|
06/15 - 05/19 Promotion in Geographie
|
||||||
|
LCRS, Philipps-Universität Marburg, Deutschland
|
||||||
|
Hauptthemen: Nebelerkennung, satellitengestützte Fernerkundung,
|
||||||
|
maschinelles Lernen
|
||||||
|
Titel der Dissertation: “Satellite-Based Fog Detection: A Dynamic Retrieval
|
||||||
|
Method for Europe Based on Machine Learning“ (Note: 1,0)
|
||||||
|
|
||||||
|
10/10 - 01/13 M.Sc. Umweltgeographie
|
||||||
|
Philipps-Universität Marburg, Deutschland
|
||||||
|
Hauptfach: Physische Geographie
|
||||||
|
Nebenfach: Informatik
|
||||||
|
Titel der Masterarbeit: “The influence of drop size distributions on the rela-
|
||||||
|
tionship between liquid water content and radar reflectivity“ (Note: 15,0)
|
||||||
|
|
||||||
|
10/08 - 09/10 B.Sc. Geographie
|
||||||
|
Philipps-Universität Marburg, Deutschland
|
||||||
|
Hauptfach: Physische Geographie
|
||||||
|
Nebenfächer: Geologie & Informatik
|
||||||
|
Zusätzliches Modul: Friedens- und Konfliktforschung
|
||||||
|
Titel der Bachelorarbeit: “Validation of the simulation results of the winter
|
||||||
|
storm Kyrill“ (Note: 13,0)
|
||||||
|
|
||||||
|
08/06 - 07/08 Bachelorstudium in Geographie Universität Zürich, Schweiz
|
||||||
|
Hauptfächer: Kartographie, Klimatologie & GIS
|
||||||
|
|
||||||
|
09/96 - 07/05 Weiterführende Schule Klettgau-Gymnasium Tiengen, Deutschland
|
||||||
|
Abschluss: Abitur (Note: 11,0)
|
||||||
|
|
||||||
|
09/92 - 07/96 Grundschule Grundschule Kadelburg, Deutschland
|
||||||
164
job_suggestor.ipynb
Normal file
164
job_suggestor.ipynb
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"id": "cc3d4ef7",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from google import genai\n",
|
||||||
|
"from google.genai import types\n",
|
||||||
|
"from datetime import datetime\n",
|
||||||
|
"from pydantic import BaseModel\n",
|
||||||
|
"import os\n",
|
||||||
|
"\n",
|
||||||
|
"class JobSuggestion(BaseModel):\n",
|
||||||
|
" company_name: str\n",
|
||||||
|
" company_description: str\n",
|
||||||
|
" job_title: str\n",
|
||||||
|
" application_text: str\n",
|
||||||
|
"\n",
|
||||||
|
"# Load CV from file\n",
|
||||||
|
"with open(\"cv_sebastian_egli.txt\", \"r\", encoding=\"utf-8\") as f:\n",
|
||||||
|
" cv = f.read()\n",
|
||||||
|
"\n",
|
||||||
|
"# Load already suggested companies from file (to avoid duplicates)\n",
|
||||||
|
"with open(\"already_suggested_companies.txt\", \"r\", encoding=\"utf-8\") as f:\n",
|
||||||
|
" already_suggested_companies = f.read()\n",
|
||||||
|
"\n",
|
||||||
|
"# Define boolean for even-numbered days (I want to switch un/solicited every day)\n",
|
||||||
|
"today = datetime.today()\n",
|
||||||
|
"unsolicited_application = today.day % 2 == 0\n",
|
||||||
|
"\n",
|
||||||
|
"if unsolicited_application:\n",
|
||||||
|
" query = [\"\"\"Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies). \n",
|
||||||
|
"Schlage mir genau ein Unternehmen in der Nähe von Marburg vor, das noch nicht in already_suggested_companies enthalten ist \n",
|
||||||
|
"und für eine Initiativbewerbung besonders gut zu meinem Profil passt. \n",
|
||||||
|
"\n",
|
||||||
|
"Erkläre in maximal 150 Wörtern prägnant, warum dieses Unternehmen eine besonders gute Wahl für mich wäre. \n",
|
||||||
|
"Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'. \n",
|
||||||
|
"Begründe die Eignung anhand meiner Qualifikationen und Interessen. \n",
|
||||||
|
"Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte, \n",
|
||||||
|
"um die Übereinstimmung zwischen mir und dem Unternehmen hervorzuheben. \n",
|
||||||
|
"\n",
|
||||||
|
"Liefere nur die Email, keine zusätzliche Erklärung.\"\"\",already_suggested_companies,cv]\n",
|
||||||
|
"else:\n",
|
||||||
|
" query = [\"\"\"Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies). \n",
|
||||||
|
"Schlage mir genau eine aktuell offene Stellenausschreibung in der Nähe von Marburg vor für ein Unternehmen, das noch nicht in already_suggested_companies enthalten ist \n",
|
||||||
|
"und die besonders gut zu meinem Profil passt. \n",
|
||||||
|
"\n",
|
||||||
|
"Erkläre in maximal 150 Wörtern prägnant, warum diese Stelle eine besonders gute Wahl für mich wäre. \n",
|
||||||
|
"Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'. \n",
|
||||||
|
"Begründe die Eignung anhand meiner Qualifikationen und Interessen. \n",
|
||||||
|
"Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte, \n",
|
||||||
|
"um die Übereinstimmung zwischen mir und der ausgeschriebenen Stelle hervorzuheben. \n",
|
||||||
|
"\n",
|
||||||
|
"Liefere nur die Email, keine zusätzliche Erklärung.\"\"\",already_suggested_companies,cv]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"id": "7c68f66d",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"api_key = os.environ.get(\"GEMINI_API_KEY\")\n",
|
||||||
|
"\n",
|
||||||
|
"client = genai.Client(api_key=api_key)\n",
|
||||||
|
"\n",
|
||||||
|
"# Step 1: grounded search (necessary for live web search)\n",
|
||||||
|
"resp_grounded = client.models.generate_content(\n",
|
||||||
|
" model=\"gemini-2.5-flash\",\n",
|
||||||
|
" contents=query,\n",
|
||||||
|
" config=types.GenerateContentConfig(\n",
|
||||||
|
" tools=[types.Tool(google_search=types.GoogleSearch())]\n",
|
||||||
|
" ),\n",
|
||||||
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"grounded_answer = resp_grounded.text # freeform answer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"id": "fe54c9a2",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Step 2: convert to JSON\n",
|
||||||
|
"resp_json = client.models.generate_content(\n",
|
||||||
|
" model=\"gemini-2.5-flash\",\n",
|
||||||
|
" contents=f\"Turn this answer into JSON: {grounded_answer}\",\n",
|
||||||
|
" config=types.GenerateContentConfig(\n",
|
||||||
|
" response_mime_type=\"application/json\",\n",
|
||||||
|
" response_schema=JobSuggestion,\n",
|
||||||
|
" ),\n",
|
||||||
|
")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "476ae4e4",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Append the response to already_suggested_companies.txt\n",
|
||||||
|
"with open(\"already_suggested_companies.txt\", \"a\", encoding=\"utf-8\") as f:\n",
|
||||||
|
" f.write(\"\\n\" + resp_json.parsed.company_name)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
|
"id": "c3d72c4f",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import smtplib\n",
|
||||||
|
"from email.mime.text import MIMEText\n",
|
||||||
|
"\n",
|
||||||
|
"gmail_key = os.environ.get(\"GMAIL_PW\")\n",
|
||||||
|
"\n",
|
||||||
|
"# Account details\n",
|
||||||
|
"sender = \"seb.egli@gmail.com\"\n",
|
||||||
|
"recipient = \"seb.egli@gmail.com\"\n",
|
||||||
|
"password = gmail_key\n",
|
||||||
|
"\n",
|
||||||
|
"# Create email\n",
|
||||||
|
"msg = MIMEText(grounded_answer)\n",
|
||||||
|
"msg[\"From\"] = sender\n",
|
||||||
|
"msg[\"To\"] = recipient\n",
|
||||||
|
"msg[\"Subject\"] = \"Neuer Jobvorschlag\"\n",
|
||||||
|
"\n",
|
||||||
|
"# Send email using Gmail's SMTP\n",
|
||||||
|
"with smtplib.SMTP_SSL(\"smtp.gmail.com\", 465) as server:\n",
|
||||||
|
" server.login(sender, password)\n",
|
||||||
|
" server.sendmail(sender, recipient, msg.as_string())"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "odc-landsat",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
100
job_suggestor.py
Normal file
100
job_suggestor.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
from google import genai
|
||||||
|
from google.genai import types
|
||||||
|
from datetime import datetime
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Load keys from environment variables
|
||||||
|
api_key = os.environ.get("GEMINI_API_KEY")
|
||||||
|
gmail_key = os.environ.get("GMAIL_PW")
|
||||||
|
|
||||||
|
class JobSuggestion(BaseModel):
|
||||||
|
company_name: str
|
||||||
|
company_description: str
|
||||||
|
job_title: str
|
||||||
|
application_text: str
|
||||||
|
|
||||||
|
# Load CV from file
|
||||||
|
with open("cv_sebastian_egli.txt", "r", encoding="utf-8") as f:
|
||||||
|
cv = f.read()
|
||||||
|
|
||||||
|
# Load already suggested companies from file (to avoid duplicates)
|
||||||
|
with open("already_suggested_companies.txt", "r", encoding="utf-8") as f:
|
||||||
|
already_suggested_companies = f.read()
|
||||||
|
|
||||||
|
# Define boolean for even-numbered days (I want to switch un/solicited every day)
|
||||||
|
today = datetime.today()
|
||||||
|
unsolicited_application = today.day % 2 == 0
|
||||||
|
|
||||||
|
if unsolicited_application:
|
||||||
|
query = ["""Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies).
|
||||||
|
Schlage mir genau ein Unternehmen in der Nähe von Marburg vor, das noch nicht in already_suggested_companies enthalten ist
|
||||||
|
und für eine Initiativbewerbung besonders gut zu meinem Profil passt.
|
||||||
|
|
||||||
|
Erkläre in maximal 150 Wörtern prägnant, warum dieses Unternehmen eine besonders gute Wahl für mich wäre.
|
||||||
|
Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'.
|
||||||
|
Begründe die Eignung anhand meiner Qualifikationen und Interessen.
|
||||||
|
Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte,
|
||||||
|
um die Übereinstimmung zwischen mir und dem Unternehmen hervorzuheben.
|
||||||
|
|
||||||
|
Liefere nur die Email, keine zusätzliche Erklärung.""",already_suggested_companies,cv]
|
||||||
|
else:
|
||||||
|
query = ["""Analysiere meinen Lebenslauf (cv) und die Liste bereits vorgeschlagener Unternehmen (already_suggested_companies).
|
||||||
|
Schlage mir genau eine aktuell offene Stellenausschreibung in der Nähe von Marburg vor für ein Unternehmen, das noch nicht in already_suggested_companies enthalten ist
|
||||||
|
und die besonders gut zu meinem Profil passt.
|
||||||
|
|
||||||
|
Erkläre in maximal 150 Wörtern prägnant, warum diese Stelle eine besonders gute Wahl für mich wäre.
|
||||||
|
Formuliere die Antwort als kurze, professionelle Email mit dem Titel: 'Neuer Jobvorschlag'.
|
||||||
|
Begründe die Eignung anhand meiner Qualifikationen und Interessen.
|
||||||
|
Füge außerdem einen einzelnen Satz hinzu, der direkt in einem Bewerbungsschreiben genutzt werden könnte,
|
||||||
|
um die Übereinstimmung zwischen mir und der ausgeschriebenen Stelle hervorzuheben.
|
||||||
|
|
||||||
|
Liefere nur die Email, keine zusätzliche Erklärung.""",already_suggested_companies,cv]
|
||||||
|
|
||||||
|
# Get response from Gemini
|
||||||
|
# Initialize Gemini client
|
||||||
|
client = genai.Client(api_key=api_key)
|
||||||
|
|
||||||
|
# Step 1: grounded search (necessary for live web search)
|
||||||
|
resp_grounded = client.models.generate_content(
|
||||||
|
model="gemini-2.5-flash",
|
||||||
|
contents=query,
|
||||||
|
config=types.GenerateContentConfig(
|
||||||
|
tools=[types.Tool(google_search=types.GoogleSearch())]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
grounded_answer = resp_grounded.text # freeform answer
|
||||||
|
|
||||||
|
# Step 2: convert to JSON
|
||||||
|
resp_json = client.models.generate_content(
|
||||||
|
model="gemini-2.5-flash",
|
||||||
|
contents=f"Turn this answer into JSON: {grounded_answer}",
|
||||||
|
config=types.GenerateContentConfig(
|
||||||
|
response_mime_type="application/json",
|
||||||
|
response_schema=JobSuggestion,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Append the response to already_suggested_companies.txt
|
||||||
|
with open("already_suggested_companies.txt", "a", encoding="utf-8") as f:
|
||||||
|
f.write("\n" + resp_json.parsed.company_name)
|
||||||
|
|
||||||
|
# Send email with the suggestion
|
||||||
|
# Account details
|
||||||
|
sender = "seb.egli@gmail.com"
|
||||||
|
recipient = "seb.egli@gmail.com"
|
||||||
|
password = gmail_key
|
||||||
|
|
||||||
|
# Create email
|
||||||
|
msg = MIMEText(grounded_answer)
|
||||||
|
msg["From"] = sender
|
||||||
|
msg["To"] = recipient
|
||||||
|
msg["Subject"] = "Neuer Jobvorschlag"
|
||||||
|
|
||||||
|
# Send email using Gmail's SMTP
|
||||||
|
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
|
||||||
|
server.login(sender, password)
|
||||||
|
server.sendmail(sender, recipient, msg.as_string())
|
||||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
google-genai
|
||||||
|
pydantic
|
||||||
Loading…
Reference in New Issue
Block a user