Spaces:
Sleeping
Sleeping
Rename main.py to app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import cv2
|
| 3 |
+
import numpy as np
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import io
|
| 6 |
+
import zipfile
|
| 7 |
+
import os
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
import tempfile
|
| 10 |
+
|
| 11 |
+
from sprite_processor import SpriteProcessor
|
| 12 |
+
from frame_detector import FrameDetector
|
| 13 |
+
from frame_namer import FrameNamer
|
| 14 |
+
|
| 15 |
+
# Page config
|
| 16 |
+
st.set_page_config(
|
| 17 |
+
page_title="AI Sprite Frame Extractor",
|
| 18 |
+
page_icon="🎮",
|
| 19 |
+
layout="wide",
|
| 20 |
+
initial_sidebar_state="expanded"
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
# Custom CSS
|
| 24 |
+
st.markdown("""
|
| 25 |
+
<style>
|
| 26 |
+
.main-header {
|
| 27 |
+
text-align: center;
|
| 28 |
+
padding: 2rem 0;
|
| 29 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 30 |
+
border-radius: 15px;
|
| 31 |
+
margin-bottom: 2rem;
|
| 32 |
+
}
|
| 33 |
+
.main-header h1 {
|
| 34 |
+
color: white;
|
| 35 |
+
font-size: 3rem;
|
| 36 |
+
margin: 0;
|
| 37 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
| 38 |
+
}
|
| 39 |
+
.main-header p {
|
| 40 |
+
color: rgba(255,255,255,0.9);
|
| 41 |
+
font-size: 1.2rem;
|
| 42 |
+
margin-top: 0.5rem;
|
| 43 |
+
}
|
| 44 |
+
.feature-card {
|
| 45 |
+
background: linear-gradient(145deg, #f5f7fa 0%, #c3cfe2 100%);
|
| 46 |
+
padding: 1.5rem;
|
| 47 |
+
border-radius: 10px;
|
| 48 |
+
margin: 1rem 0;
|
| 49 |
+
border-left: 4px solid #667eea;
|
| 50 |
+
}
|
| 51 |
+
.stButton>button {
|
| 52 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 53 |
+
color: white;
|
| 54 |
+
font-weight: bold;
|
| 55 |
+
padding: 0.75rem 2rem;
|
| 56 |
+
border-radius: 25px;
|
| 57 |
+
border: none;
|
| 58 |
+
font-size: 1.1rem;
|
| 59 |
+
transition: all 0.3s ease;
|
| 60 |
+
}
|
| 61 |
+
.stButton>button:hover {
|
| 62 |
+
transform: translateY(-2px);
|
| 63 |
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
| 64 |
+
}
|
| 65 |
+
.success-box {
|
| 66 |
+
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
| 67 |
+
color: white;
|
| 68 |
+
padding: 1rem;
|
| 69 |
+
border-radius: 10px;
|
| 70 |
+
text-align: center;
|
| 71 |
+
}
|
| 72 |
+
.frame-preview {
|
| 73 |
+
border: 2px solid #667eea;
|
| 74 |
+
border-radius: 8px;
|
| 75 |
+
padding: 5px;
|
| 76 |
+
margin: 5px;
|
| 77 |
+
}
|
| 78 |
+
</style>
|
| 79 |
+
""", unsafe_allow_html=True)
|
| 80 |
+
|
| 81 |
+
# Header
|
| 82 |
+
st.markdown("""
|
| 83 |
+
<div class="main-header">
|
| 84 |
+
<h1>🎮 AI Sprite Frame Extractor</h1>
|
| 85 |
+
<p>استخرج وحسّن إطارات Sprite Sheets بذكاء اصطناعي</p>
|
| 86 |
+
</div>
|
| 87 |
+
""", unsafe_allow_html=True)
|
| 88 |
+
|
| 89 |
+
# Sidebar
|
| 90 |
+
with st.sidebar:
|
| 91 |
+
st.markdown("## ⚙️ الإعدادات")
|
| 92 |
+
|
| 93 |
+
st.markdown("### 🔧 خيارات المعالجة")
|
| 94 |
+
|
| 95 |
+
enhance_quality = st.checkbox("✨ تحسين الجودة (Upscale)", value=True,
|
| 96 |
+
help="رفع دقة الصورة وإزالة الضبابية")
|
| 97 |
+
|
| 98 |
+
if enhance_quality:
|
| 99 |
+
scale_factor = st.slider("📏 معامل التكبير", 2, 4, 4,
|
| 100 |
+
help="2x = ضعف الحجم، 4x = أربع أضعاف")
|
| 101 |
+
else:
|
| 102 |
+
scale_factor = 1
|
| 103 |
+
|
| 104 |
+
auto_detect = st.checkbox("🤖 اكتشاف تلقائي للإطارات", value=True,
|
| 105 |
+
help="تحديد الإطارات تلقائياً")
|
| 106 |
+
|
| 107 |
+
if not auto_detect:
|
| 108 |
+
manual_frames = st.number_input("عدد الإطارات", min_value=1, max_value=50, value=8)
|
| 109 |
+
|
| 110 |
+
smart_naming = st.checkbox("🏷️ تسمية ذكية", value=True,
|
| 111 |
+
help="تحديد نوع الحركة تلقائياً")
|
| 112 |
+
|
| 113 |
+
padding = st.slider("📐 الهوامش الداخلية", 0, 20, 2,
|
| 114 |
+
help="إضافة هوامش حول كل إطار")
|
| 115 |
+
|
| 116 |
+
st.markdown("---")
|
| 117 |
+
st.markdown("### 📊 معلومات")
|
| 118 |
+
st.info("💡 **نصيحة:** تأكد من أن الصورة بخلفية شفافة للحصول على أفضل نتائج")
|
| 119 |
+
|
| 120 |
+
# Main content
|
| 121 |
+
st.markdown("## 📤 رفع الصورة")
|
| 122 |
+
|
| 123 |
+
uploaded_file = st.file_uploader(
|
| 124 |
+
"اختر صورة Sprite Sheet",
|
| 125 |
+
type=['png', 'jpg', 'jpeg', 'webp'],
|
| 126 |
+
help="صورة تحتوي على إطارات متحركة متتالية"
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
if uploaded_file is not None:
|
| 130 |
+
# Read image
|
| 131 |
+
file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
|
| 132 |
+
original_image = cv2.imdecode(file_bytes, cv2.IMREAD_UNCHANGED)
|
| 133 |
+
|
| 134 |
+
# Display original
|
| 135 |
+
col1, col2 = st.columns(2)
|
| 136 |
+
|
| 137 |
+
with col1:
|
| 138 |
+
st.markdown("### 🖼️ الصورة الأصلية")
|
| 139 |
+
|
| 140 |
+
# Convert for display
|
| 141 |
+
if len(original_image.shape) == 3 and original_image.shape[2] == 4:
|
| 142 |
+
display_img = cv2.cvtColor(original_image, cv2.COLOR_BGRA2RGBA)
|
| 143 |
+
else:
|
| 144 |
+
display_img = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
|
| 145 |
+
|
| 146 |
+
st.image(display_img, use_column_width=True)
|
| 147 |
+
|
| 148 |
+
st.markdown(f"""
|
| 149 |
+
**الأبعاد:** {original_image.shape[1]} × {original_image.shape[0]} px
|
| 150 |
+
**القنوات:** {original_image.shape[2] if len(original_image.shape) == 3 else 1}
|
| 151 |
+
""")
|
| 152 |
+
|
| 153 |
+
# Process button
|
| 154 |
+
st.markdown("---")
|
| 155 |
+
|
| 156 |
+
if st.button("🚀 بدء المعالجة", use_container_width=True):
|
| 157 |
+
with st.spinner("⏳ جاري المعالجة... قد يستغرق هذا ب��ع دقائق"):
|
| 158 |
+
|
| 159 |
+
# Initialize processors
|
| 160 |
+
progress_bar = st.progress(0)
|
| 161 |
+
status_text = st.empty()
|
| 162 |
+
|
| 163 |
+
# Step 1: Enhance quality
|
| 164 |
+
if enhance_quality:
|
| 165 |
+
status_text.text("✨ جاري تحسين جودة الصورة...")
|
| 166 |
+
processor = SpriteProcessor()
|
| 167 |
+
enhanced_image = processor.enhance_image(original_image, scale_factor)
|
| 168 |
+
progress_bar.progress(25)
|
| 169 |
+
else:
|
| 170 |
+
enhanced_image = original_image
|
| 171 |
+
progress_bar.progress(25)
|
| 172 |
+
|
| 173 |
+
# Step 2: Detect frames
|
| 174 |
+
status_text.text("🔍 جاري اكتشاف الإطارات...")
|
| 175 |
+
detector = FrameDetector()
|
| 176 |
+
|
| 177 |
+
if auto_detect:
|
| 178 |
+
frames, frame_boxes = detector.detect_frames_auto(enhanced_image, padding)
|
| 179 |
+
else:
|
| 180 |
+
frames, frame_boxes = detector.detect_frames_manual(enhanced_image, manual_frames, padding)
|
| 181 |
+
|
| 182 |
+
progress_bar.progress(60)
|
| 183 |
+
|
| 184 |
+
# Step 3: Smart naming
|
| 185 |
+
if smart_naming:
|
| 186 |
+
status_text.text("🏷️ جاري تحليل وتسمية الإطارات...")
|
| 187 |
+
namer = FrameNamer()
|
| 188 |
+
frame_names = namer.name_frames(frames)
|
| 189 |
+
else:
|
| 190 |
+
frame_names = [f"frame_{i:03d}" for i in range(len(frames))]
|
| 191 |
+
|
| 192 |
+
progress_bar.progress(80)
|
| 193 |
+
|
| 194 |
+
# Step 4: Create ZIP
|
| 195 |
+
status_text.text("📦 جاري إنشاء ملف ZIP...")
|
| 196 |
+
zip_buffer = io.BytesIO()
|
| 197 |
+
|
| 198 |
+
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
| 199 |
+
for i, (frame, name) in enumerate(zip(frames, frame_names)):
|
| 200 |
+
# Convert frame to PNG bytes
|
| 201 |
+
if len(frame.shape) == 3 and frame.shape[2] == 4:
|
| 202 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGRA2RGBA)
|
| 203 |
+
else:
|
| 204 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 205 |
+
|
| 206 |
+
pil_img = Image.fromarray(frame_rgb)
|
| 207 |
+
img_buffer = io.BytesIO()
|
| 208 |
+
pil_img.save(img_buffer, format='PNG')
|
| 209 |
+
img_buffer.seek(0)
|
| 210 |
+
|
| 211 |
+
zip_file.writestr(f"{name}.png", img_buffer.getvalue())
|
| 212 |
+
|
| 213 |
+
# Add info file
|
| 214 |
+
info_content = f"""Sprite Frame Extractor - Export Report
|
| 215 |
+
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 216 |
+
Total Frames: {len(frames)}
|
| 217 |
+
Original Size: {original_image.shape[1]}x{original_image.shape[0]}
|
| 218 |
+
Enhanced: {'Yes (' + str(scale_factor) + 'x)' if enhance_quality else 'No'}
|
| 219 |
+
|
| 220 |
+
Frame List:
|
| 221 |
+
"""
|
| 222 |
+
for i, name in enumerate(frame_names):
|
| 223 |
+
info_content += f" {i+1}. {name}.png\n"
|
| 224 |
+
|
| 225 |
+
zip_file.writestr("info.txt", info_content)
|
| 226 |
+
|
| 227 |
+
zip_buffer.seek(0)
|
| 228 |
+
progress_bar.progress(100)
|
| 229 |
+
status_text.empty()
|
| 230 |
+
|
| 231 |
+
# Display results
|
| 232 |
+
with col2:
|
| 233 |
+
st.markdown("### ✅ النتيجة")
|
| 234 |
+
|
| 235 |
+
if enhance_quality:
|
| 236 |
+
st.markdown(f"**الأبعاد بعد التحسين:** {enhanced_image.shape[1]} × {enhanced_image.shape[0]} px")
|
| 237 |
+
|
| 238 |
+
st.markdown(f"**عدد الإطارات المكتشفة:** {len(frames)}")
|
| 239 |
+
|
| 240 |
+
# Show frame previews
|
| 241 |
+
st.markdown("### 👁️ معاينة الإطارات")
|
| 242 |
+
|
| 243 |
+
preview_cols = st.columns(min(4, len(frames)))
|
| 244 |
+
for i, (frame, name) in enumerate(zip(frames[:8], frame_names[:8])):
|
| 245 |
+
with preview_cols[i % 4]:
|
| 246 |
+
if len(frame.shape) == 3 and frame.shape[2] == 4:
|
| 247 |
+
display_frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2RGBA)
|
| 248 |
+
else:
|
| 249 |
+
display_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 250 |
+
|
| 251 |
+
st.image(display_frame, caption=name, use_column_width=True)
|
| 252 |
+
|
| 253 |
+
if len(frames) > 8:
|
| 254 |
+
st.info(f"... و {len(frames) - 8} إطارات أخرى")
|
| 255 |
+
|
| 256 |
+
# Download button
|
| 257 |
+
st.markdown("---")
|
| 258 |
+
st.markdown("### 📥 التحميل")
|
| 259 |
+
|
| 260 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 261 |
+
|
| 262 |
+
st.download_button(
|
| 263 |
+
label="⬇️ تحميل ملف ZIP",
|
| 264 |
+
data=zip_buffer,
|
| 265 |
+
file_name=f"sprite_frames_{timestamp}.zip",
|
| 266 |
+
mime="application/zip",
|
| 267 |
+
use_container_width=True
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
st.markdown("""
|
| 271 |
+
<div class="success-box">
|
| 272 |
+
<h3>🎉 تمت المعالجة بنج��ح!</h3>
|
| 273 |
+
<p>تم استخراج {} إطار وجاهزة للاستخدام</p>
|
| 274 |
+
</div>
|
| 275 |
+
""".format(len(frames)), unsafe_allow_html=True)
|
| 276 |
+
|
| 277 |
+
else:
|
| 278 |
+
# Show features when no image uploaded
|
| 279 |
+
st.markdown("---")
|
| 280 |
+
st.markdown("## ✨ مميزات الأداة")
|
| 281 |
+
|
| 282 |
+
col1, col2, col3 = st.columns(3)
|
| 283 |
+
|
| 284 |
+
with col1:
|
| 285 |
+
st.markdown("""
|
| 286 |
+
<div class="feature-card">
|
| 287 |
+
<h3>🔍 تحسين الجودة</h3>
|
| 288 |
+
<p>استخدام تقنية Real-ESRGAN لرفع دقة الصور وإزالة الضبابية مع الحفاظ على جودة البكسل</p>
|
| 289 |
+
</div>
|
| 290 |
+
""", unsafe_allow_html=True)
|
| 291 |
+
|
| 292 |
+
with col2:
|
| 293 |
+
st.markdown("""
|
| 294 |
+
<div class="feature-card">
|
| 295 |
+
<h3>🤖 اكتشاف ذكي</h3>
|
| 296 |
+
<p>اكتشاف الإطارات تلقائياً باستخدام خوارزميات متقدمة للرؤية الحاسوبية</p>
|
| 297 |
+
</div>
|
| 298 |
+
""", unsafe_allow_html=True)
|
| 299 |
+
|
| 300 |
+
with col3:
|
| 301 |
+
st.markdown("""
|
| 302 |
+
<div class="feature-card">
|
| 303 |
+
<h3>🏷️ تسمية ذكية</h3>
|
| 304 |
+
<p>تحديد نوع الحركة (Idle, Run, Attack...) تلقائياً باستخدام نماذج التعلم العميق</p>
|
| 305 |
+
</div>
|
| 306 |
+
""", unsafe_allow_html=True)
|
| 307 |
+
|
| 308 |
+
# Example section
|
| 309 |
+
st.markdown("---")
|
| 310 |
+
st.markdown("## 📋 كيفية الاستخدام")
|
| 311 |
+
|
| 312 |
+
st.markdown("""
|
| 313 |
+
1. **📤 ارفع الصورة** - اختر ملف Sprite Sheet بخلفية شفافة
|
| 314 |
+
2. **⚙️ اضبط الإعدادات** - فعّل خيارات التحسين والتسمية الذكية حسب الحاجة
|
| 315 |
+
3. **🚀 ابدأ المعالجة** - اضغط على زر المعالجة وانتظر قليلاً
|
| 316 |
+
4. **📥 حمل النتيجة** - احصل على ملف ZIP يحتوي على جميع الإطارات
|
| 317 |
+
""")
|
| 318 |
+
|
| 319 |
+
st.markdown("---")
|
| 320 |
+
st.info("💡 **ملاحظة:** للحصول على أفضل النتائج، تأكد من أن الصورة بخلفية شفافة (PNG مع قناة ألفا)")
|
main.py
DELETED
|
@@ -1,233 +0,0 @@
|
|
| 1 |
-
import discord
|
| 2 |
-
from discord.ext import commands, tasks
|
| 3 |
-
from discord import app_commands
|
| 4 |
-
import io
|
| 5 |
-
import aiohttp
|
| 6 |
-
from aiohttp import web # أضفنا مكتبة الويب
|
| 7 |
-
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
|
| 8 |
-
import datetime
|
| 9 |
-
import random
|
| 10 |
-
import traceback
|
| 11 |
-
import os
|
| 12 |
-
|
| 13 |
-
# ==========================================
|
| 14 |
-
# 1. إعدادات البوت الأساسية والمعرفات
|
| 15 |
-
# ==========================================
|
| 16 |
-
SERVER_ID = 1485773565772173367
|
| 17 |
-
ADMIN_ROLE_ID = 1488187501142216826
|
| 18 |
-
WELCOME_CHANNEL_ID = 1485773566736597125
|
| 19 |
-
LOGS_CHANNEL_ID = 1485773566736597129
|
| 20 |
-
CONTROL_CHANNEL_ID = 1485773566736597127
|
| 21 |
-
|
| 22 |
-
WELCOME_MESSAGES = [
|
| 23 |
-
"مرحباً بك في مجتمعنا! نتمنى لك وقتاً ممتعاً.",
|
| 24 |
-
"أهلاً بك في خادمنا، نورت المكان!",
|
| 25 |
-
"سعداء بانضمامك إلينا، مرحباً بك!",
|
| 26 |
-
"يا هلا ومرحب! خطوة عزيزة في سيرفرنا."
|
| 27 |
-
]
|
| 28 |
-
|
| 29 |
-
POLYGON_COORDS = [
|
| 30 |
-
(227, 307), (251, 299), (286, 291), (318, 297), (344, 297),
|
| 31 |
-
(371, 297), (397, 297), (429, 299), (461, 302), (493, 299),
|
| 32 |
-
(509, 321), (522, 355), (533, 387), (530, 427), (528, 475),
|
| 33 |
-
(520, 517), (509, 546), (482, 562), (445, 581), (403, 594),
|
| 34 |
-
(350, 602), (304, 597), (259, 578), (222, 546), (193, 504),
|
| 35 |
-
(188, 459), (185, 411), (193, 374), (209, 334)
|
| 36 |
-
]
|
| 37 |
-
|
| 38 |
-
daily_stats = {"joins": 0, "leaves": 0, "errors": 0}
|
| 39 |
-
|
| 40 |
-
intents = discord.Intents.default()
|
| 41 |
-
intents.members = True
|
| 42 |
-
intents.message_content = True
|
| 43 |
-
bot = commands.Bot(command_prefix="!", intents=intents)
|
| 44 |
-
|
| 45 |
-
# ==========================================
|
| 46 |
-
# 2. خادم الويب الوهمي لـ Hugging Face
|
| 47 |
-
# ==========================================
|
| 48 |
-
async def web_server():
|
| 49 |
-
app = web.Application()
|
| 50 |
-
app.router.add_get('/', lambda r: web.Response(text="Bot is running smoothly!"))
|
| 51 |
-
runner = web.AppRunner(app)
|
| 52 |
-
await runner.setup()
|
| 53 |
-
site = web.TCPSite(runner, '0.0.0.0', 7860)
|
| 54 |
-
await site.start()
|
| 55 |
-
print("🌐 تم تشغيل خادم الويب الوهمي بنجاح على المنفذ 7860 (Hugging Face سيصبح Running الآن)")
|
| 56 |
-
|
| 57 |
-
# ==========================================
|
| 58 |
-
# 3. دوال مساعدة (معالجة الصور)
|
| 59 |
-
# ==========================================
|
| 60 |
-
async def generate_welcome_image(member: discord.Member):
|
| 61 |
-
async with aiohttp.ClientSession() as session:
|
| 62 |
-
bg_url = "https://i.ibb.co/fVzTX4sr/c24b17434824edeab1c3ce02bef9072f.jpg"
|
| 63 |
-
async with session.get(bg_url) as resp:
|
| 64 |
-
bg_bytes = await resp.read()
|
| 65 |
-
bg_image = Image.open(io.BytesIO(bg_bytes)).convert("RGBA")
|
| 66 |
-
|
| 67 |
-
avatar_url = member.display_avatar.with_format("png").with_size(512).url
|
| 68 |
-
async with session.get(avatar_url) as resp:
|
| 69 |
-
avatar_bytes = await resp.read()
|
| 70 |
-
avatar_image = Image.open(io.BytesIO(avatar_bytes)).convert("RGBA")
|
| 71 |
-
|
| 72 |
-
avatar_image = avatar_image.resize((360, 360))
|
| 73 |
-
|
| 74 |
-
mask = Image.new("L", bg_image.size, 0)
|
| 75 |
-
draw = ImageDraw.Draw(mask)
|
| 76 |
-
draw.polygon(POLYGON_COORDS, fill=255)
|
| 77 |
-
|
| 78 |
-
avatar_layer = Image.new("RGBA", bg_image.size, (0, 0, 0, 0))
|
| 79 |
-
avatar_layer.paste(avatar_image, (185, 290))
|
| 80 |
-
|
| 81 |
-
purple_filter = Image.new("RGBA", avatar_layer.size, (128, 0, 128, 60))
|
| 82 |
-
avatar_layer = Image.alpha_composite(avatar_layer, purple_filter)
|
| 83 |
-
|
| 84 |
-
final_image = Image.composite(avatar_layer, bg_image, mask)
|
| 85 |
-
|
| 86 |
-
draw_final = ImageDraw.Draw(final_image)
|
| 87 |
-
try:
|
| 88 |
-
font = ImageFont.truetype("arial.ttf", 36)
|
| 89 |
-
except:
|
| 90 |
-
font = ImageFont.load_default()
|
| 91 |
-
|
| 92 |
-
text = f"#{member.name}"
|
| 93 |
-
draw_final.text((368, 630), text, fill=(100, 0, 150), font=font, anchor="mm")
|
| 94 |
-
|
| 95 |
-
final_buffer = io.BytesIO()
|
| 96 |
-
final_image.save(final_buffer, format="PNG")
|
| 97 |
-
final_buffer.seek(0)
|
| 98 |
-
return final_buffer
|
| 99 |
-
|
| 100 |
-
# ==========================================
|
| 101 |
-
# 4. أحداث البوت (Events)
|
| 102 |
-
# ==========================================
|
| 103 |
-
@bot.event
|
| 104 |
-
async def setup_hook():
|
| 105 |
-
# تشغيل خادم الويب في الخلفية مع بداية عمل البوت
|
| 106 |
-
bot.loop.create_task(web_server())
|
| 107 |
-
|
| 108 |
-
@bot.event
|
| 109 |
-
async def on_ready():
|
| 110 |
-
print(f"✅ تم تسجيل الدخول كـ {bot.user}")
|
| 111 |
-
daily_stats_task.start()
|
| 112 |
-
try:
|
| 113 |
-
synced = await bot.tree.sync()
|
| 114 |
-
print(f"🔄 تم مزامنة {len(synced)} أوامر سلاش.")
|
| 115 |
-
except Exception as e:
|
| 116 |
-
print(f"❌ خطأ في المزامنة: {e}")
|
| 117 |
-
|
| 118 |
-
@bot.event
|
| 119 |
-
async def on_member_join(member: discord.Member):
|
| 120 |
-
if member.guild.id != SERVER_ID:
|
| 121 |
-
return
|
| 122 |
-
|
| 123 |
-
daily_stats["joins"] += 1
|
| 124 |
-
channel = bot.get_channel(WELCOME_CHANNEL_ID)
|
| 125 |
-
|
| 126 |
-
if channel:
|
| 127 |
-
try:
|
| 128 |
-
image_buffer = await generate_welcome_image(member)
|
| 129 |
-
file = discord.File(fp=image_buffer, filename="welcome.png")
|
| 130 |
-
|
| 131 |
-
embed = discord.Embed(
|
| 132 |
-
title="🎉 عضو جديد انضم إلينا!",
|
| 133 |
-
description=random.choice(WELCOME_MESSAGES),
|
| 134 |
-
color=discord.Color.purple()
|
| 135 |
-
)
|
| 136 |
-
embed.add_field(name="👤 العضو", value=member.mention, inline=True)
|
| 137 |
-
embed.add_field(name="📅 تاريخ الانضمام", value=f"<t:{int(datetime.datetime.now().timestamp())}:d>", inline=True)
|
| 138 |
-
embed.add_field(name="📈 رقم العضو", value=f"أنت العضو رقم {member.guild.member_count}", inline=False)
|
| 139 |
-
embed.set_image(url="attachment://welcome.png")
|
| 140 |
-
|
| 141 |
-
msg = await channel.send(content=member.mention, embed=embed, file=file)
|
| 142 |
-
await msg.add_reaction("👋")
|
| 143 |
-
|
| 144 |
-
logs_channel = bot.get_channel(LOGS_CHANNEL_ID)
|
| 145 |
-
if logs_channel:
|
| 146 |
-
await logs_channel.send(f"✅ **دخول:** {member.mention} انضم للسيرفر.")
|
| 147 |
-
|
| 148 |
-
except Exception as e:
|
| 149 |
-
daily_stats["errors"] += 1
|
| 150 |
-
logs_channel = bot.get_channel(LOGS_CHANNEL_ID)
|
| 151 |
-
if logs_channel:
|
| 152 |
-
await logs_channel.send(f"❌ **خطأ في الترحيب (تأكد من صلاحيات رفع الملفات للسيرفر):** `{e}`")
|
| 153 |
-
|
| 154 |
-
@bot.event
|
| 155 |
-
async def on_member_remove(member: discord.Member):
|
| 156 |
-
if member.guild.id != SERVER_ID:
|
| 157 |
-
return
|
| 158 |
-
daily_stats["leaves"] += 1
|
| 159 |
-
logs_channel = bot.get_channel(LOGS_CHANNEL_ID)
|
| 160 |
-
if logs_channel:
|
| 161 |
-
await logs_channel.send(f"🚪 **خروج:** {member.name} غادر السيرفر.")
|
| 162 |
-
|
| 163 |
-
# ==========================================
|
| 164 |
-
# 5. المهام المجدولة (Tasks)
|
| 165 |
-
# ==========================================
|
| 166 |
-
@tasks.loop(hours=24)
|
| 167 |
-
async def daily_stats_task():
|
| 168 |
-
logs_channel = bot.get_channel(LOGS_CHANNEL_ID)
|
| 169 |
-
if logs_channel:
|
| 170 |
-
joins = daily_stats["joins"]
|
| 171 |
-
leaves = daily_stats["leaves"]
|
| 172 |
-
growth = joins - leaves
|
| 173 |
-
|
| 174 |
-
embed = discord.Embed(title="📊 إحصائيات الـ 24 ساعة الماضية", color=discord.Color.blue())
|
| 175 |
-
embed.add_field(name="📥 الدخول", value=str(joins), inline=True)
|
| 176 |
-
embed.add_field(name="📤 الخروج", value=str(leaves), inline=True)
|
| 177 |
-
embed.add_field(name="📈 نسبة النمو", value=f"{growth} أعضاء", inline=False)
|
| 178 |
-
embed.add_field(name="❌ الأخطاء", value=str(daily_stats["errors"]), inline=False)
|
| 179 |
-
|
| 180 |
-
await logs_channel.send(embed=embed)
|
| 181 |
-
|
| 182 |
-
daily_stats["joins"] = 0
|
| 183 |
-
daily_stats["leaves"] = 0
|
| 184 |
-
daily_stats["errors"] = 0
|
| 185 |
-
|
| 186 |
-
@daily_stats_task.before_loop
|
| 187 |
-
async def before_daily_stats():
|
| 188 |
-
await bot.wait_until_ready()
|
| 189 |
-
|
| 190 |
-
# ==========================================
|
| 191 |
-
# 6. أوامر السلاش (Slash Commands)
|
| 192 |
-
# ==========================================
|
| 193 |
-
@bot.tree.command(name="تجربة_ترحيب", description="تجربة نظام الترحيب على عضو معين")
|
| 194 |
-
@app_commands.describe(member="العضو الذي تريد تجربة الترحيب عليه")
|
| 195 |
-
async def test_welcome(interaction: discord.Interaction, member: discord.Member):
|
| 196 |
-
if interaction.channel_id != CONTROL_CHANNEL_ID:
|
| 197 |
-
return await interaction.response.send_message("❌ لا يمكنك استخدام هذا الأمر هنا.", ephemeral=True)
|
| 198 |
-
|
| 199 |
-
if not any(role.id == ADMIN_ROLE_ID for role in interaction.user.roles) and interaction.user.id != interaction.guild.owner_id:
|
| 200 |
-
return await interaction.response.send_message("❌ لا تملك صلاحية استخدام هذا الأمر.", ephemeral=True)
|
| 201 |
-
|
| 202 |
-
await interaction.response.defer(ephemeral=True)
|
| 203 |
-
|
| 204 |
-
try:
|
| 205 |
-
channel = bot.get_channel(WELCOME_CHANNEL_ID)
|
| 206 |
-
image_buffer = await generate_welcome_image(member)
|
| 207 |
-
file = discord.File(fp=image_buffer, filename="welcome.png")
|
| 208 |
-
|
| 209 |
-
embed = discord.Embed(
|
| 210 |
-
title="🎉 عضو جديد انضم إلينا! (تجربة)",
|
| 211 |
-
description=random.choice(WELCOME_MESSAGES),
|
| 212 |
-
color=discord.Color.purple()
|
| 213 |
-
)
|
| 214 |
-
embed.add_field(name="👤 العضو", value=member.mention, inline=True)
|
| 215 |
-
embed.add_field(name="📅 تاريخ الانضمام", value=f"<t:{int(datetime.datetime.now().timestamp())}:d>", inline=True)
|
| 216 |
-
embed.add_field(name="📈 رقم العضو", value=f"أنت العضو رقم {member.guild.member_count}", inline=False)
|
| 217 |
-
embed.set_image(url="attachment://welcome.png")
|
| 218 |
-
|
| 219 |
-
msg = await channel.send(content=member.mention, embed=embed, file=file)
|
| 220 |
-
await msg.add_reaction("👋")
|
| 221 |
-
|
| 222 |
-
await interaction.followup.send("✅ تم إرسال رسالة التجربة بنجاح في قناة الترحيب.")
|
| 223 |
-
except Exception as e:
|
| 224 |
-
await interaction.followup.send(f"❌ حدث خطأ أثناء التجربة: {e}\n(يرجى التأكد من أن البوت يمتلك صلاحية إرفاق الملفات في إعدادات السيرفر والقناة)")
|
| 225 |
-
|
| 226 |
-
# ==========================================
|
| 227 |
-
# 7. تشغيل البوت
|
| 228 |
-
# ==========================================
|
| 229 |
-
TOKEN = os.environ.get("BOT_TOKEN")
|
| 230 |
-
if TOKEN:
|
| 231 |
-
bot.run(TOKEN)
|
| 232 |
-
else:
|
| 233 |
-
print("❌ خطأ: ��م يتم العثور على التوكن. يرجى إضافة BOT_TOKEN إلى بيئة العمل.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|