Initial commit: Hermes Agent skills collection

- Trading skills (OKX, dividend, lottery, quantitative)
- Creative skills (ASCII art, diagrams, video)
- Development skills (GitHub, debugging, TDD)
- Research skills (arXiv, blog monitoring)
- Productivity skills (email, documents, notes)
- MCP integration skills
- Custom user skills
This commit is contained in:
Hermes Skills Manager
2026-07-05 02:31:15 -04:00
commit 6770bc9b9d
908 changed files with 239614 additions and 0 deletions
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""
TG信号监听器(no_agent模式):
从Telegram forwarder数据库读取新信号→调advisor脚本→格式化含📐→推QQ
用法: python3 tg_signal_monitor.py
配合cron: */1 * * * * python3 ~/.hermes/skills/trading/okx-auto-position/scripts/tg_signal_monitor.py
"""
import sqlite3
import json
import subprocess
import sys
import os
import re
from pathlib import Path
from datetime import datetime
# Paths
FORWARDER_DB = "/tmp/forward.db" # TG forwarder DB (docker cp出来)
STATE_FILE = Path.home() / ".hermes/trading/.signal_monitor_state"
SKILL_DIR = Path.home() / ".hermes/skills/trading/okx-auto-position"
ADVISOR_SCRIPT = SKILL_DIR / "scripts" / "okx_position_advisor.py"
FORMAT_SCRIPT = SKILL_DIR / "scripts" / "format_signal.py"
QQ_PUSH = Path.home() / ".hermes/scripts/push_to_qq.sh"
SIGNAL_HISTORY_DB = Path.home() / ".hermes/trading/signal_history.db"
def get_last_msg_id():
"""Read last processed message ID"""
if STATE_FILE.exists():
return int(STATE_FILE.read_text().strip())
return 0
def save_last_msg_id(msg_id):
"""Save last processed message ID"""
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
STATE_FILE.write_text(str(msg_id))
def parse_signal(text):
"""Parse TG signal text, extract key fields"""
# Extract trader name
trader_match = re.search(r'【([^】]{1,20})】', text)
trader = trader_match.group(1) if trader_match else "未知"
# Extract fields
fields = {}
patterns = {
'symbol': r'【币种】\s*[:]?\s*(\S+)',
'side': r'【方向】\s*[:]?\s*(做多|做空)',
'leverage': r'【杠杆】\s*[:]?\s*(\d+)',
'size': r'【仓位大小】\s*[:]?\s*([\d,.]+)',
'value': r'【仓位价值】\s*[:]?\s*\$?\s*([\d,.]+)',
'entry': r'【开仓价】\s*[:]?\s*([\d,.]+)',
'current': r'【当前价】\s*[:]?\s*([\d,.]+)',
'pnl': r'【未实现盈亏】\s*[:]?\s*([-\d,.]+)',
}
for key, pattern in patterns.items():
match = re.search(pattern, text)
if match:
fields[key] = match.group(1).replace(',', '')
return trader, fields
def classify_signal(fields, current_positions):
"""Classify as A(加仓) or C(新开仓)"""
symbol = fields.get('symbol', '').replace('USDT', '').replace('/USDT', '').strip()
for pos in current_positions:
if symbol.upper() in pos['symbol'].upper():
return 'A' # 加仓
return 'C' # 新开仓
def run_format_script(fields, trader, signal_type):
"""Run format_signal.py and return the formatted message"""
symbol = fields.get('symbol', '').replace('USDT', '').replace('/USDT', '').strip()
side = 'long' if fields.get('side', '').startswith('做多') else 'short'
leverage = fields.get('leverage', '10')
size = fields.get('size', '0')
value = fields.get('value', '$0')
entry = fields.get('entry', '0')
pnl = fields.get('pnl', '0')
if not value.startswith('$'):
value = f'${value}'
cmd = [
'python3', str(FORMAT_SCRIPT),
'--symbol', symbol,
'--side', side,
'--leverage', leverage,
'--trader', trader,
'--trader-pos', f'{size} {symbol}',
'--trader-value', value,
'--trader-entry', entry,
'--trader-pnl', pnl,
'--signal-type', signal_type,
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
return result.stdout.strip()
else:
return f"⚠️ format_signal.py 错误: {result.stderr.strip()}"
except subprocess.TimeoutExpired:
return "⚠️ format_signal.py 超时"
except Exception as e:
return f"⚠️ 执行错误: {e}"
def push_to_qq(message):
"""Push message to QQ via push_to_qq.sh"""
try:
result = subprocess.run(
['bash', str(QQ_PUSH), message],
capture_output=True, text=True, timeout=15
)
return result.returncode == 0
except:
return False
def log_to_db(trader, symbol, side, leverage, raw_text, outcome='pushed'):
"""Log signal to history database"""
try:
conn = sqlite3.connect(str(SIGNAL_HISTORY_DB))
conn.execute("""
INSERT INTO signals (timestamp, trader, symbol, side, leverage, raw_text, outcome)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (datetime.now().isoformat(), trader, symbol, side, leverage, raw_text, outcome))
conn.commit()
conn.close()
except:
pass
def main():
# Check if forwarder DB exists
if not Path(FORWARDER_DB).exists():
# Try to copy from docker
try:
subprocess.run(
['docker', 'cp', 'telegram-forwarder:/app/db/forward.db', FORWARDER_DB],
capture_output=True, timeout=10
)
except:
print("❌ 无法获取forwarder DB")
return
last_id = get_last_msg_id()
try:
conn = sqlite3.connect(FORWARDER_DB)
conn.row_factory = sqlite3.Row
# Get new messages from the forwarder
cursor = conn.execute("""
SELECT id, message_text, created_at
FROM forwarded_messages
WHERE id > ? AND chat_id = '-1003966251111'
ORDER BY id ASC
LIMIT 10
""", (last_id,))
messages = cursor.fetchall()
conn.close()
if not messages:
return # No new messages, silent exit
for msg in messages:
text = msg['message_text'] or ''
msg_id = msg['id']
# Skip non-signal messages
if '【币种】' not in text and '【方向】' not in text:
save_last_msg_id(msg_id)
continue
# Parse signal
trader, fields = parse_signal(text)
if not fields.get('symbol') or not fields.get('side'):
save_last_msg_id(msg_id)
continue
# Classify (simplified - always treat as new for now)
signal_type = 'C'
# Run format_signal.py
message = run_format_script(fields, trader, signal_type)
if message and '⚠️' not in message:
# Push to QQ
success = push_to_qq(message)
# Log to DB
symbol = fields.get('symbol', '').replace('USDT', '').strip()
side = 'long' if fields.get('side', '').startswith('做多') else 'short'
log_to_db(trader, symbol, side, fields.get('leverage', '10'), text,
'pushed' if success else 'push_failed')
save_last_msg_id(msg_id)
except sqlite3.OperationalError as e:
print(f"❌ DB错误: {e}")
except Exception as e:
print(f"❌ 错误: {e}")
if __name__ == '__main__':
main()