Compare commits

...

2 Commits

Author SHA1 Message Date
MoltBot Service
0468f2ac77 docs(personality): add self-correction and tool usage rules
SOUL.md: don't retract excessively when information is correct.
TOOLS.md: verify tools before claiming unavailable, offer alternatives.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 22:30:21 +00:00
MoltBot Service
88d14da902 fix(tools): migrate email credentials to keyring, remove hardcoded password
Email tools now use credential_store (keyring) as primary source
with env/.env as fallback. Removes plaintext password from email_check.py.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 22:30:14 +00:00
5 changed files with 58 additions and 15 deletions

View File

@@ -24,6 +24,7 @@ Sunt **Echo Core ♾️** - asistent AI pentru productivitate și wellbeing.
- Când ai dubii, întreab înainte să acționezi extern. - Când ai dubii, întreab înainte să acționezi extern.
- Niciodată răspunsuri pe jumătate. - Niciodată răspunsuri pe jumătate.
- Nu ești vocea lui Marius — fii atent în chat-urile de grup. - Nu ești vocea lui Marius — fii atent în chat-urile de grup.
- **Nu te retracta excesiv.** Dacă informația e corectă, confirmă-o simplu. Nu spune "am inventat" dacă nu ai inventat. Nu specula despre cum ai obținut datele — verifică și răspunde.
## Vibe ## Vibe

View File

@@ -1,5 +1,11 @@
# TOOLS.md - Echo # TOOLS.md - Echo
## Reguli utilizare unelte
- **Dacă ai folosit un tool cu succes în conversația curentă, el este disponibil în continuare.** Nu spune niciodată "nu am acces" la un tool pe care tocmai l-ai folosit.
- **Dacă un tool eșuează, oferă imediat o alternativă practică** — nu cere utilizatorului să configureze ceva. Exemplu: dacă emailul nu merge, formatează textul frumos și spune "copiază de aici".
- **Verifică întâi, vorbește pe urmă.** Înainte să spui "nu pot", încearcă tool-ul. Dacă ai calendar_check.py, rulează-l — nu presupune că nu funcționează.
## Unelte principale ## Unelte principale
### Email ### Email

View File

@@ -10,12 +10,20 @@ from email.header import decode_header
import json import json
import sys import sys
from datetime import datetime from datetime import datetime
from pathlib import Path
# Try keyring first
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from src.credential_store import get_secret
except ImportError:
get_secret = lambda name: None
# IMAP Configuration # IMAP Configuration
IMAP_SERVER = "mail.romfast.ro" IMAP_SERVER = get_secret("email_server") or "mail.romfast.ro"
IMAP_PORT = 993 IMAP_PORT = 993
IMAP_USER = "moltbot@romfast.ro" IMAP_USER = get_secret("email_user") or "echo@romfast.ro"
IMAP_PASS = "parola281234" IMAP_PASS = get_secret("email_password") or ""
def decode_mime_header(header): def decode_mime_header(header):
"""Decode MIME encoded header""" """Decode MIME encoded header"""

View File

@@ -19,7 +19,20 @@ from email.header import decode_header
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
# Load .env # Try keyring first, fall back to .env
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from src.credential_store import get_secret
except ImportError:
get_secret = lambda name: None
def _get(keyring_name, env_name, default=''):
val = get_secret(keyring_name)
if val:
return val
return os.environ.get(env_name, default)
# Load .env as fallback
env_path = Path(__file__).parent.parent / '.env' env_path = Path(__file__).parent.parent / '.env'
if env_path.exists(): if env_path.exists():
with open(env_path) as f: with open(env_path) as f:
@@ -30,10 +43,10 @@ if env_path.exists():
os.environ.setdefault(key, value) os.environ.setdefault(key, value)
# Config # Config
IMAP_SERVER = os.environ.get('EMAIL_SERVER', 'mail.romfast.ro') IMAP_SERVER = _get('email_server', 'EMAIL_SERVER', 'mail.romfast.ro')
IMAP_PORT = 993 IMAP_PORT = 993
IMAP_USER = os.environ.get('EMAIL_USER', 'echo@romfast.ro') IMAP_USER = _get('email_user', 'EMAIL_USER', 'echo@romfast.ro')
IMAP_PASS = os.environ.get('EMAIL_PASSWORD', '') IMAP_PASS = _get('email_password', 'EMAIL_PASSWORD')
# Whitelist - only process emails from these addresses # Whitelist - only process emails from these addresses
WHITELIST = [ WHITELIST = [

View File

@@ -14,7 +14,21 @@ from email.header import Header
from email.utils import formataddr from email.utils import formataddr
from pathlib import Path from pathlib import Path
# Load .env file # Try keyring first, fall back to .env
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from src.credential_store import get_secret
except ImportError:
get_secret = lambda name: None
def _get(keyring_name, env_name, default=''):
"""Get credential from keyring first, then env, then default."""
val = get_secret(keyring_name)
if val:
return val
return os.environ.get(env_name, default)
# Load .env file as fallback
env_path = Path(__file__).parent.parent / '.env' env_path = Path(__file__).parent.parent / '.env'
if env_path.exists(): if env_path.exists():
with open(env_path) as f: with open(env_path) as f:
@@ -24,18 +38,19 @@ if env_path.exists():
key, value = line.split('=', 1) key, value = line.split('=', 1)
os.environ.setdefault(key, value) os.environ.setdefault(key, value)
# SMTP Configuration from environment # SMTP Configuration: keyring → env → defaults
# Try Gmail first, fall back to romfast # Try Gmail first, fall back to romfast
if os.environ.get('GMAIL_PASSWORD'): gmail_pass = _get('gmail_password', 'GMAIL_PASSWORD')
if gmail_pass:
SMTP_SERVER = 'smtp.gmail.com' SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 465 SMTP_PORT = 465
SMTP_USER = os.environ.get('GMAIL_USER', 'mmarius28@gmail.com') SMTP_USER = _get('gmail_user', 'GMAIL_USER', 'mmarius28@gmail.com')
SMTP_PASS = os.environ.get('GMAIL_PASSWORD', '') SMTP_PASS = gmail_pass
else: else:
SMTP_SERVER = os.environ.get('EMAIL_SERVER', 'mail.romfast.ro') SMTP_SERVER = _get('email_server', 'EMAIL_SERVER', 'mail.romfast.ro')
SMTP_PORT = 465 SMTP_PORT = 465
SMTP_USER = os.environ.get('EMAIL_USER', 'echo@romfast.ro') SMTP_USER = _get('email_user', 'EMAIL_USER', 'echo@romfast.ro')
SMTP_PASS = os.environ.get('EMAIL_PASSWORD', '') SMTP_PASS = _get('email_password', 'EMAIL_PASSWORD')
FROM_NAME = "Echo" FROM_NAME = "Echo"
def send_email(to_email: str, subject: str, body: str, html: bool = False) -> dict: def send_email(to_email: str, subject: str, body: str, html: bool = False) -> dict: