Compare commits
13 Commits
alert-auto
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c628443c8 | |||
| bbedb98ce0 | |||
| 3b12eb5e21 | |||
| 747458d7f3 | |||
| fe8ab74bd3 | |||
| 354ea793c0 | |||
| 41e7c6bac5 | |||
| 211e8dc6c7 | |||
| 6634ea7ad6 | |||
| 0c5c27a660 | |||
| db90774e17 | |||
| d549c43844 | |||
| 668f56020d |
31
.github/workflows/deploy.yml
vendored
Normal file
31
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Deploy ChatGPT-Discord-Bot
|
||||
on:
|
||||
workflow_dispatch:
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: quocanh
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# cd to /vps/chatgptdsc and do docker compose down then docker compose pull the docker compose up -d
|
||||
steps:
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: cd to deployment directory
|
||||
run: cd /home/vps/chatgptdsc
|
||||
|
||||
- name: Pull latest images
|
||||
run: docker compose -f /home/vps/chatgptdsc/docker-compose.yml pull
|
||||
|
||||
- name: Stop existing services
|
||||
run: docker compose -f /home/vps/chatgptdsc/docker-compose.yml down
|
||||
|
||||
- name: Start services
|
||||
run: docker compose -f /home/vps/chatgptdsc/docker-compose.yml up -d
|
||||
137
.github/workflows/main.yml
vendored
137
.github/workflows/main.yml
vendored
@@ -1,122 +1,37 @@
|
||||
name: Build and Deploy ChatGPT-Discord-Bot
|
||||
name: Build & Push Docker to Gitea Registry
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: git.quocanh.me
|
||||
OWNER: coder-vippro
|
||||
IMAGE: chatgpt-discord-bot
|
||||
TAG: latest
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MONGODB_URI: ${{ secrets.MONGODB_URI }}
|
||||
environment: Private Server Deploy
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest # khuyên dùng runner của bạn
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
with:
|
||||
python-version: '3.13.2'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Login to Gitea registry
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pytest pytest-cov flake8
|
||||
pip install -r requirements.txt
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" \
|
||||
| docker login "$REGISTRY" \
|
||||
-u "${{ secrets.REGISTRY_USERNAME }}" \
|
||||
--password-stdin
|
||||
|
||||
- name: Run unit tests with coverage
|
||||
run: |
|
||||
python -m pytest tests/ --cov=src
|
||||
|
||||
- name: Check dependencies for security issues
|
||||
uses: pyupio/safety-action@v1.0.1
|
||||
with:
|
||||
api-key: ${{ secrets.SAFETY_API_KEY }}
|
||||
|
||||
build-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
environment: Private Server Deploy
|
||||
needs: tests
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
- name: Setup buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image for amd64
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
tags: ghcr.io/coder-vippro/chatgpt-discord-bot:latest-amd64
|
||||
|
||||
build-arm64:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
environment: Private Server Deploy
|
||||
needs: tests
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image for arm64
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/arm64
|
||||
tags: ghcr.io/coder-vippro/chatgpt-discord-bot:latest-arm64
|
||||
|
||||
merge-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-amd64, build-arm64]
|
||||
steps:
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push multi-arch manifest
|
||||
- name: Build & push
|
||||
run: |
|
||||
docker buildx imagetools create -t ghcr.io/coder-vippro/chatgpt-discord-bot:latest \
|
||||
ghcr.io/coder-vippro/chatgpt-discord-bot:latest-amd64 \
|
||||
ghcr.io/coder-vippro/chatgpt-discord-bot:latest-arm64
|
||||
|
||||
deploy-notification:
|
||||
runs-on: ubuntu-latest
|
||||
needs: merge-manifest
|
||||
if: ${{ success() }}
|
||||
steps:
|
||||
- name: Send deployment notification
|
||||
uses: sarisia/actions-status-discord@v1
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
title: "✅ New deployment successful!"
|
||||
description: |
|
||||
Image: ${{ needs.build-and-push.outputs.image }}:${{ needs.build-and-push.outputs.version }}
|
||||
Commit: ${{ github.sha }}
|
||||
Repository: ${{ github.repository }}
|
||||
color: 0x00ff00
|
||||
username: GitHub Actions
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
-t $REGISTRY/$OWNER/$IMAGE:$TAG \
|
||||
--push \
|
||||
.
|
||||
|
||||
@@ -10,12 +10,12 @@ dnspython>=2.5.0
|
||||
|
||||
# Web & HTTP
|
||||
aiohttp>=3.9.0
|
||||
requests>=2.31.0
|
||||
requests>=2.32.5
|
||||
beautifulsoup4>=4.12.0
|
||||
|
||||
# AI & ML
|
||||
runware>=0.4.33
|
||||
tiktoken>=0.7.0
|
||||
tiktoken>=0.12.0
|
||||
|
||||
# Data Processing
|
||||
pandas>=2.1.0
|
||||
@@ -46,4 +46,4 @@ ruff>=0.3.0
|
||||
|
||||
# Monitoring & Logging (Optional)
|
||||
# sentry-sdk>=1.40.0 # Uncomment for error monitoring
|
||||
# python-json-logger>=2.0.0 # Uncomment for structured logging
|
||||
# python-json-logger>=2.0.0 # Uncomment for structured logging
|
||||
|
||||
@@ -12,6 +12,7 @@ You have access to a powerful code interpreter environment that allows you to:
|
||||
- Execute Python code in a secure, isolated environment
|
||||
- Maximum execution time: 60 seconds
|
||||
- Output limit: 100KB
|
||||
- ⚠️ **IMPORTANT: Use print() to display results!** Only printed output is captured and shown to the user.
|
||||
|
||||
## 📦 **Package Management (Auto-Install)**
|
||||
The code interpreter can AUTOMATICALLY install missing packages when needed!
|
||||
@@ -43,18 +44,64 @@ import seaborn as sns # Will auto-install if missing
|
||||
import pandas as pd # Will auto-install if missing
|
||||
|
||||
df = pd.DataFrame({'x': [1,2,3], 'y': [4,5,6]})
|
||||
print(df) # ⚠️ Use print() to show output!
|
||||
sns.scatterplot(data=df, x='x', y='y')
|
||||
plt.savefig('plot.png')
|
||||
print("Chart saved!") # Confirm completion
|
||||
```
|
||||
|
||||
⚠️ **REMINDER: Only printed output is visible!** Always use print() for any data you want the user to see.
|
||||
|
||||
## 📁 **File Management (48-Hour Lifecycle)**
|
||||
|
||||
### **User-Uploaded Files**
|
||||
- Users can upload files (CSV, Excel, JSON, images, etc.)
|
||||
- Files are stored with unique `file_id`
|
||||
- Access files using: `df = load_file('file_id_here')`
|
||||
- Files expire after 48 hours automatically
|
||||
|
||||
### **CRITICAL: How to Load Files**
|
||||
|
||||
**Option 1: load_file() - Returns data directly (RECOMMENDED)**
|
||||
```python
|
||||
# For CSV files - returns DataFrame directly, DO NOT pass to pd.read_csv()!
|
||||
# ⚠️ Use the ACTUAL file_id from the upload message, NOT this example!
|
||||
df = load_file('<file_id_from_upload_message>')
|
||||
print(df.head()) # Works immediately!
|
||||
```
|
||||
|
||||
**Option 2: get_file_path() - Returns path for manual loading**
|
||||
```python
|
||||
# If you need the actual file path:
|
||||
path = get_file_path('<file_id_from_upload_message>')
|
||||
df = pd.read_csv(path)
|
||||
```
|
||||
|
||||
### **COMMON MISTAKES TO AVOID**
|
||||
```python
|
||||
# ❌ WRONG - load_file() returns a DataFrame, NOT a path!
|
||||
file_path = load_file('<file_id>')
|
||||
df = pd.read_csv(file_path) # ERROR: Cannot read DataFrame as CSV!
|
||||
|
||||
# ❌ WRONG - file_id is NOT a file path!
|
||||
df = pd.read_csv('<file_id>') # ERROR: File not found!
|
||||
|
||||
# ❌ WRONG - Using example IDs from documentation!
|
||||
df = load_file('example_from_docs') # ERROR: Use REAL file_id from upload!
|
||||
|
||||
# ✅ CORRECT - use load_file() with the ACTUAL file_id from upload message
|
||||
df = load_file('<file_id_from_upload_message>') # Copy exact ID from 📁 FILE UPLOADED
|
||||
print(df.head()) # ⚠️ Use print() to show output!
|
||||
print(df.describe())
|
||||
|
||||
# ✅ CORRECT - use get_file_path() if you need the path
|
||||
path = get_file_path('<file_id_from_upload_message>')
|
||||
df = pd.read_csv(path)
|
||||
print(df.info()) # Always print results!
|
||||
```
|
||||
|
||||
⚠️ CRITICAL: The file_id is shown in the conversation when a file is uploaded.
|
||||
Look for: "📁 FILE UPLOADED" or "df = load_file('...')" in recent messages!
|
||||
|
||||
### **Generated Files**
|
||||
- ANY file you create is captured and saved
|
||||
- Supported types: images, CSVs, text, JSON, HTML, PDFs, etc. (80+ formats)
|
||||
@@ -94,10 +141,14 @@ plt.savefig('plot.png')
|
||||
|
||||
**Load uploaded file:**
|
||||
```python
|
||||
# User uploaded 'sales_data.csv' with file_id: 'user_123_1234567890_abc123'
|
||||
df = load_file('user_123_1234567890_abc123')
|
||||
print(df.head())
|
||||
print(f"Loaded {len(df)} rows")
|
||||
# ⚠️ Find the ACTUAL file_id in the conversation's "📁 FILE UPLOADED" message!
|
||||
# DO NOT copy this example - use the real file_id shown when the user uploaded!
|
||||
df = load_file('<paste_actual_file_id_here>')
|
||||
|
||||
# ⚠️ CRITICAL: Always use print() to display results!
|
||||
print(df.head()) # Show first rows
|
||||
print(df.describe()) # Show statistics
|
||||
print(f"Loaded {len(df)} rows, {len(df.columns)} columns")
|
||||
```
|
||||
|
||||
**Create multiple output files:**
|
||||
|
||||
@@ -195,9 +195,35 @@ NORMAL_CHAT_PROMPT = """You're ChatGPT for Discord. Be concise, helpful, safe. R
|
||||
TOOLS:
|
||||
1. google_search(query) - Web search for current info
|
||||
2. scrape_webpage(url) - Extract webpage content
|
||||
3. execute_python_code(code) - Run Python, packages auto-install. Use load_file('file_id') for user files. Save outputs to files.
|
||||
3. execute_python_code(code) - Run Python, packages auto-install. **FILE ACCESS: See critical instructions below!**
|
||||
4. set_reminder(content, time) / get_reminders() - Manage reminders
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
⚠️ CRITICAL: FILE ACCESS IN CODE INTERPRETER
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
When users upload files, you will see a message like:
|
||||
📁 FILE UPLOADED - USE THIS FILE_ID:
|
||||
Filename: data.csv
|
||||
⚠️ TO ACCESS THIS FILE IN CODE, YOU MUST USE:
|
||||
df = load_file('<THE_ACTUAL_FILE_ID_FROM_CONTEXT>')
|
||||
|
||||
**IMPORTANT: Copy the EXACT file_id from the file upload message - do NOT use examples!**
|
||||
|
||||
✅ CORRECT:
|
||||
df = load_file('<file_id_from_upload_message>')
|
||||
print(df.head()) # Use print() to show output!
|
||||
|
||||
⚠️ IMPORTANT: Always use print() to display results - code output is only captured via print()!
|
||||
|
||||
❌ WRONG - Using filename:
|
||||
df = pd.read_csv('data.csv') # FAILS - file not found!
|
||||
|
||||
❌ WRONG - Using example file_id from prompts:
|
||||
df = load_file('example_id_from_docs') # FAILS - use the REAL ID!
|
||||
|
||||
⚠️ CRITICAL: Look for the 📁 FILE UPLOADED message in this conversation and copy the EXACT file_id shown there!
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
IMAGE GENERATION & EDITING TOOLS
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -1222,11 +1222,20 @@ print("\\n=== Correlation Analysis ===")
|
||||
user_message = message.content.strip() if message.content else ""
|
||||
|
||||
file_context = (
|
||||
f"\n\n[User uploaded file: {filename}]\n"
|
||||
f"[File ID: {file_id}]\n"
|
||||
f"[File Type: {file_type}]\n"
|
||||
f"[Size: {size_str}]\n"
|
||||
f"[Available in code_interpreter via: load_file('{file_id}')]\n"
|
||||
f"\n\n══════════════════════════════════════════════\n"
|
||||
f"📁 FILE UPLOADED - USE THIS FILE_ID:\n"
|
||||
f"══════════════════════════════════════════════\n"
|
||||
f"Filename: {filename}\n"
|
||||
f"File Type: {file_type}\n"
|
||||
f"Size: {size_str}\n"
|
||||
f"\n"
|
||||
f"⚠️ TO ACCESS THIS FILE IN CODE, YOU MUST USE:\n"
|
||||
f" df = load_file('{file_id}')\n"
|
||||
f"\n"
|
||||
f"❌ DO NOT use the filename directly (e.g., pd.read_csv('{filename}'))\n"
|
||||
f"❌ DO NOT use file_id as a path (e.g., pd.read_csv('{file_id}'))\n"
|
||||
f"✅ ONLY use: load_file('{file_id}')\n"
|
||||
f"══════════════════════════════════════════════\n"
|
||||
)
|
||||
|
||||
if user_message:
|
||||
|
||||
@@ -197,7 +197,7 @@ BLOCKED_PATTERNS = [
|
||||
r'gc\.',
|
||||
r'sys\.getsizeof',
|
||||
r'sys\.getrefcount',
|
||||
r'id\s*\(', # Block id() which can leak memory addresses
|
||||
r'\bid\s*\(', # Block id() which can leak memory addresses (\b ensures word boundary)
|
||||
]
|
||||
|
||||
# Additional patterns that log warnings but don't block
|
||||
@@ -1050,31 +1050,57 @@ import os
|
||||
|
||||
FILES = {json.dumps(file_paths_map)}
|
||||
|
||||
def get_file_path(file_id):
|
||||
'''
|
||||
Get the actual file path for a given file ID.
|
||||
Use this to get the path for pd.read_csv(), open(), etc.
|
||||
|
||||
Args:
|
||||
file_id: The file ID provided when the file was uploaded
|
||||
|
||||
Returns:
|
||||
str: The actual file path on disk
|
||||
|
||||
Example:
|
||||
path = get_file_path('878573881449906208_1764556246_bdbaecc8')
|
||||
df = pd.read_csv(path)
|
||||
|
||||
Available files: Use list(FILES.keys()) to see available files
|
||||
'''
|
||||
if file_id not in FILES:
|
||||
raise ValueError(f"File '{{file_id}}' not found. Available: {{list(FILES.keys())}}")
|
||||
return FILES[file_id]
|
||||
|
||||
def load_file(file_id):
|
||||
'''
|
||||
Load a file automatically based on its extension.
|
||||
Supports 200+ file types with smart auto-detection.
|
||||
Load a file automatically based on its extension and return the data directly.
|
||||
DO NOT pass the result to pd.read_csv() - it already returns a DataFrame!
|
||||
|
||||
Args:
|
||||
file_id: The file ID provided when the file was uploaded
|
||||
|
||||
Returns:
|
||||
Loaded file data (varies by file type):
|
||||
- CSV/TSV: pandas DataFrame
|
||||
- CSV/TSV: pandas DataFrame (ready to use!)
|
||||
- Excel (.xlsx, .xls): pandas ExcelFile object
|
||||
- JSON: pandas DataFrame or dict
|
||||
- Parquet/Feather: pandas DataFrame
|
||||
- Text files: string content
|
||||
- Images: PIL Image object
|
||||
- And 200+ more formats...
|
||||
|
||||
Excel file usage examples:
|
||||
excel_file = load_file('file_id')
|
||||
sheet_names = excel_file.sheet_names
|
||||
df = excel_file.parse('Sheet1')
|
||||
df2 = pd.read_excel(excel_file, sheet_name='Sheet1')
|
||||
CORRECT usage for CSV:
|
||||
df = load_file('file_id') # Returns DataFrame directly
|
||||
print(df.head())
|
||||
|
||||
Available files: {{', '.join(FILES.keys()) if FILES else 'None'}}
|
||||
WRONG usage (DO NOT DO THIS):
|
||||
file_path = load_file('file_id') # WRONG! This is a DataFrame, not a path
|
||||
df = pd.read_csv(file_path) # This will FAIL!
|
||||
|
||||
If you need the file path instead, use get_file_path():
|
||||
path = get_file_path('file_id')
|
||||
df = pd.read_csv(path)
|
||||
|
||||
Available files: Use list(FILES.keys()) to see available files
|
||||
'''
|
||||
if file_id not in FILES:
|
||||
available_files = list(FILES.keys())
|
||||
|
||||
@@ -190,7 +190,7 @@ def capture_exception(
|
||||
"""
|
||||
logger.exception(f"Captured exception: {exception}")
|
||||
|
||||
if SENTRY_AVAILABLE and sentry_sdk.Hub.current.client:
|
||||
if SENTRY_AVAILABLE and sentry_sdk.is_initialized():
|
||||
with sentry_sdk.push_scope() as scope:
|
||||
if context:
|
||||
for key, value in context.items():
|
||||
@@ -219,7 +219,7 @@ def capture_message(
|
||||
log_method = getattr(logger, level, logger.info)
|
||||
log_method(message)
|
||||
|
||||
if SENTRY_AVAILABLE and sentry_sdk.Hub.current.client:
|
||||
if SENTRY_AVAILABLE and sentry_sdk.is_initialized():
|
||||
with sentry_sdk.push_scope() as scope:
|
||||
if context:
|
||||
for key, value in context.items():
|
||||
@@ -242,7 +242,7 @@ def set_user_context(
|
||||
username: Discord username
|
||||
guild_id: Discord guild ID
|
||||
"""
|
||||
if SENTRY_AVAILABLE and sentry_sdk.Hub.current.client:
|
||||
if SENTRY_AVAILABLE and sentry_sdk.is_initialized():
|
||||
sentry_sdk.set_user({
|
||||
"id": str(user_id),
|
||||
"username": username,
|
||||
@@ -325,7 +325,7 @@ async def measure_async(name: str, **metadata):
|
||||
|
||||
# Start Sentry transaction if available
|
||||
transaction = None
|
||||
if SENTRY_AVAILABLE and sentry_sdk.Hub.current.client:
|
||||
if SENTRY_AVAILABLE and sentry_sdk.is_initialized():
|
||||
transaction = sentry_sdk.start_transaction(
|
||||
op="task",
|
||||
name=name
|
||||
|
||||
Reference in New Issue
Block a user