• 25-03-2026, 23:56:05
    #1

    pip install customtkinter openpyxl pandas python-dotenv netgsm-sms
    import customtkinter as ctk
    from tkinter import filedialog, messagebox, scrolledtext
    import pandas as pd
    import threading
    import time
    import re
    from datetime import datetime
    import os
    from dotenv import load_dotenv, set_key
    from netgsm import Netgsm
    from netgsm.exceptions.api_exception import ApiException, NotAcceptableException
    from netgsm.utils.enums import Encoding
    
    ctk.set_appearance_mode("dark")
    ctk.set_default_color_theme("blue")
    
    ENV_FILE = ".env"
    
    class NetgsmSMSBot:
        def __init__(self):
            self.window = ctk.CTk()
            self.window.title("Netgsm Toplu SMS Botu")
            self.window.geometry("950x750")
            
            self.excel_file = None
            self.contacts = []
            self.is_sending = False
            self.netgsm_client = None
            
            load_dotenv(ENV_FILE)
            
            self.setup_ui()
            
        def setup_ui(self):
            self.tabview = ctk.CTkTabview(self.window, width=900, height=700)
            self.tabview.pack(padx=20, pady=20, fill="both", expand=True)
            
            self.tabview.add("📨 SMS Gönder")
            self.tabview.add("⚙️ Netgsm Ayarları")
            
            self.setup_send_tab()
            self.setup_settings_tab()
        
        def setup_send_tab(self):
            send_tab = self.tabview.tab("📨 SMS Gönder")
            
            excel_frame = ctk.CTkFrame(send_tab)
            excel_frame.pack(fill="x", padx=20, pady=(20,10))
            
            self.excel_label = ctk.CTkLabel(excel_frame, text="📁 Excel dosyası seçilmedi", font=("Roboto",12))
            self.excel_label.pack(side="left", padx=(0,10))
            
            select_btn = ctk.CTkButton(excel_frame, text="Excel Seç", command=self.select_excel, width=120)
            select_btn.pack(side="right")
            
            msg_frame = ctk.CTkFrame(send_tab)
            msg_frame.pack(fill="both", expand=True, padx=20, pady=10)
            
            ctk.CTkLabel(msg_frame, text="Mesaj Şablonu:", font=("Roboto",14, "bold")).pack(anchor="w")
            self.message_text = ctk.CTkTextbox(msg_frame, height=150)
            self.message_text.pack(fill="both", expand=True, pady=5)
            self.message_text.insert("1.0", "Merhaba ${isim},\nNetgsm ile toplu SMS gönderimi testidir.\nTeşekkürler.")
            
            tip_label = ctk.CTkLabel(msg_frame, text="💡 İpucu: ${isim} ve ${soyisim} değişkenlerini kullanabilirsiniz",
                                     font=("Roboto",10), text_color="gray")
            tip_label.pack(anchor="w")
            
            prog_frame = ctk.CTkFrame(send_tab)
            prog_frame.pack(fill="x", padx=20, pady=10)
            
            self.progress_bar = ctk.CTkProgressBar(prog_frame)
            self.progress_bar.pack(fill="x", pady=5)
            self.progress_bar.set(0)
            
            self.progress_label = ctk.CTkLabel(prog_frame, text="Hazır", font=("Roboto",12))
            self.progress_label.pack()
            
            log_frame = ctk.CTkFrame(send_tab)
            log_frame.pack(fill="both", expand=True, padx=20, pady=10)
            
            ctk.CTkLabel(log_frame, text="İşlem Günlüğü:", font=("Roboto",14, "bold")).pack(anchor="w")
            self.log_text = scrolledtext.ScrolledText(log_frame, height=12, bg="#2b2b2b", fg="white",
                                                      font=("Consolas",10))
            self.log_text.pack(fill="both", expand=True, pady=5)
            
            self.send_btn = ctk.CTkButton(send_tab, text="📨 SMS Göndermeyi Başlat",
                                          command=self.start_sending, height=45,
                                          font=("Roboto",14,"bold"))
            self.send_btn.pack(pady=(10,20), padx=20, fill="x")
        
        def setup_settings_tab(self):
            settings_tab = self.tabview.tab("⚙️ Netgsm Ayarları")
            
            info_frame = ctk.CTkFrame(settings_tab)
            info_frame.pack(fill="x", padx=20, pady=20)
            ctk.CTkLabel(info_frame, text="Netgsm Hesap Bilgileri", font=("Roboto",16,"bold")).pack(pady=5)
            ctk.CTkLabel(info_frame, text="Bu bilgiler .env dosyasında saklanacaktır.", 
                         font=("Roboto",10), text_color="gray").pack()
            
            user_frame = ctk.CTkFrame(settings_tab)
            user_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(user_frame, text="Kullanıcı Adı:", width=120).pack(side="left", padx=5)
            self.username_entry = ctk.CTkEntry(user_frame, width=300)
            self.username_entry.pack(side="left", padx=5)
            self.username_entry.insert(0, os.getenv("NETGSM_USERNAME", ""))
            
            pass_frame = ctk.CTkFrame(settings_tab)
            pass_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(pass_frame, text="Şifre:", width=120).pack(side="left", padx=5)
            self.password_entry = ctk.CTkEntry(pass_frame, width=300, show="*")
            self.password_entry.pack(side="left", padx=5)
            self.password_entry.insert(0, os.getenv("NETGSM_PASSWORD", ""))
            
            header_frame = ctk.CTkFrame(settings_tab)
            header_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(header_frame, text="Gönderici Başlığı:", width=120).pack(side="left", padx=5)
            self.header_entry = ctk.CTkEntry(header_frame, width=300)
            self.header_entry.pack(side="left", padx=5)
            self.header_entry.insert(0, os.getenv("NETGSM_MSGHEADER", ""))
            
            test_frame = ctk.CTkFrame(settings_tab)
            test_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(test_frame, text="Test Numarası:", width=120).pack(side="left", padx=5)
            self.testno_entry = ctk.CTkEntry(test_frame, width=300)
            self.testno_entry.pack(side="left", padx=5)
            self.testno_entry.insert(0, os.getenv("NETGSM_TEST_NUMBER", ""))
            ctk.CTkLabel(test_frame, text="(10 haneli, 0'sız)", font=("Roboto",9), text_color="gray").pack(side="left", padx=5)
            
            btn_frame = ctk.CTkFrame(settings_tab)
            btn_frame.pack(fill="x", padx=20, pady=20)
            
            save_btn = ctk.CTkButton(btn_frame, text="💾 Bilgileri Kaydet", command=self.save_settings, width=150)
            save_btn.pack(side="left", padx=10)
            
            test_btn = ctk.CTkButton(btn_frame, text="🔍 Test SMS Gönder", command=self.test_sms, width=150, fg_color="green")
            test_btn.pack(side="left", padx=10)
            
            self.settings_status = ctk.CTkLabel(settings_tab, text="", font=("Roboto",11))
            self.settings_status.pack(pady=10)
        
        def select_excel(self):
            filename = filedialog.askopenfilename(filetypes=[("Excel files","*.xlsx *.xls")])
            if filename:
                self.excel_file = filename
                self.excel_label.configure(text=f"📄 {os.path.basename(filename)}")
                self.log(f"Excel dosyası seçildi: {filename}")
                try:
                    self.load_contacts()
                except Exception as e:
                    self.log(f"❌ Excel okuma hatası: {str(e)}")
        
        def load_contacts(self):
            df = pd.read_excel(self.excel_file, engine='openpyxl')
            self.contacts = []
            for _, row in df.iterrows():
                phone = str(row.get('telefon', row.get('phone', ''))).strip()
                phone = re.sub(r'\D', '', phone)
                if phone.startswith('0') and len(phone) == 11:
                    phone = phone[1:]
                if len(phone) == 10:
                    phone = phone
                else:
                    self.log(f"⚠️ Geçersiz numara formatı: {phone}")
                    continue
                self.contacts.append({
                    'phone': phone,
                    'first_name': str(row.get('isim', row.get('first_name', ''))).strip(),
                    'last_name': str(row.get('soyisim', row.get('last_name', ''))).strip()
                })
            self.log(f"✅ {len(self.contacts)} kişi yüklendi.")
        
        def save_settings(self):
            username = self.username_entry.get().strip()
            password = self.password_entry.get().strip()
            header = self.header_entry.get().strip()
            testno = self.testno_entry.get().strip()
            
            if not username or not password or not header:
                messagebox.showerror("Hata", "Kullanıcı adı, şifre ve gönderici başlığı zorunludur.")
                return
            
            set_key(ENV_FILE, "NETGSM_USERNAME", username)
            set_key(ENV_FILE, "NETGSM_PASSWORD", password)
            set_key(ENV_FILE, "NETGSM_MSGHEADER", header)
            if testno:
                set_key(ENV_FILE, "NETGSM_TEST_NUMBER", testno)
            
            load_dotenv(ENV_FILE, override=True)
            
            self.settings_status.configure(text="✅ Bilgiler başarıyla kaydedildi.", text_color="green")
            self.log("Netgsm ayarları kaydedildi.")
        
        def test_sms(self):
            self.save_settings()
            
            username = os.getenv("NETGSM_USERNAME")
            password = os.getenv("NETGSM_PASSWORD")
            header = os.getenv("NETGSM_MSGHEADER")
            testno = os.getenv("NETGSM_TEST_NUMBER")
            
            if not testno:
                messagebox.showerror("Hata", "Test numarası girilmemiş.")
                return
            
            testno = re.sub(r'\D', '', testno)
            if testno.startswith('0'):
                testno = testno[1:]
            if len(testno) != 10:
                messagebox.showerror("Hata", "Test numarası 10 haneli olmalı (başında 0 olmadan).")
                return
            
            try:
                client = Netgsm(username=username, password=password)
                message = {
                    "msg": "Bu bir test mesajıdır. Netgsm SDK çalışıyor.",
                    "no": testno
                }
                response = client.sms.send(msgheader=header, messages=[message], encoding=Encoding.TR)
                self.log(f"✅ Test SMS gönderildi! JobID: {response.get('jobid')}")
                messagebox.showinfo("Başarılı", f"Test SMS gönderildi.\nJobID: {response.get('jobid')}")
            except NotAcceptableException as e:
                self.log(f"❌ Netgsm hatası: {e.message} (Kod: {e.code})")
                messagebox.showerror("Netgsm Hatası", f"{e.message}\nKod: {e.code}")
            except ApiException as e:
                self.log(f"❌ API hatası: {e.message}")
                messagebox.showerror("API Hatası", e.message)
        
        def send_sms_via_sdk(self, phone, message):
            username = os.getenv("NETGSM_USERNAME")
            password = os.getenv("NETGSM_PASSWORD")
            header = os.getenv("NETGSM_MSGHEADER")
            
            if not all([username, password, header]):
                return False, "API bilgileri eksik. Ayarlar sekmesini kontrol edin."
            
            try:
                client = Netgsm(username=username, password=password)
                msg_obj = {
                    "msg": message,
                    "no": phone
                }
                response = client.sms.send(msgheader=header, messages=[msg_obj], encoding=Encoding.TR)
                if response.get('jobid'):
                    return True, f"JobID: {response.get('jobid')}"
                else:
                    return False, "Bilinmeyen yanıt"
            except NotAcceptableException as e:
                return False, f"{e.message} (Kod: {e.code})"
            except ApiException as e:
                return False, e.message
            except Exception as e:
                return False, str(e)
        
        def send_bulk(self, message_template):
            total = len(self.contacts)
            success = 0
            fail = 0
            
            for i, contact in enumerate(self.contacts):
                if not self.is_sending:
                    break
                
                msg = message_template
                msg = msg.replace('${isim}', contact['first_name'])
                msg = msg.replace('${soyisim}', contact['last_name'])
                
                ok, info = self.send_sms_via_sdk(contact['phone'], msg)
                if ok:
                    success += 1
                    self.log(f"✅ Gönderildi: {contact['phone']} - {contact['first_name']} ({info})")
                else:
                    fail += 1
                    self.log(f"❌ Başarısız: {contact['phone']} - {info}")
                
                progress = (i+1)/total
                self.progress_bar.set(progress)
                self.progress_label.configure(text=f"{i+1}/{total} - Başarılı: {success}, Başarısız: {fail}")
                time.sleep(0.5)
            
            self.log(f"\n{'='*50}\n📊 RAPOR\nToplam: {total}\nBaşarılı: {success}\nBaşarısız: {fail}\n{'='*50}")
            messagebox.showinfo("Tamamlandı", f"Gönderim bitti.\nBaşarılı: {success}\nBaşarısız: {fail}")
        
        def start_sending(self):
            if self.is_sending:
                messagebox.showwarning("Uyarı", "Zaten gönderim devam ediyor.")
                return
            if not self.excel_file:
                messagebox.showwarning("Uyarı", "Önce bir Excel dosyası seçin.")
                return
            if not self.contacts:
                self.load_contacts()
                if not self.contacts:
                    messagebox.showerror("Hata", "Excel'de geçerli numara bulunamadı.")
                    return
            
            msg_template = self.message_text.get("1.0", "end-1c").strip()
            if not msg_template:
                messagebox.showwarning("Uyarı", "Mesaj şablonu boş olamaz.")
                return
            
            if not all([os.getenv("NETGSM_USERNAME"), os.getenv("NETGSM_PASSWORD"), os.getenv("NETGSM_MSGHEADER")]):
                messagebox.showwarning("Uyarı", "Netgsm API bilgileri eksik. Lütfen ayarlar sekmesinden girin.")
                self.tabview.set("⚙️ Netgsm Ayarları")
                return
            
            self.is_sending = True
            self.send_btn.configure(state="disabled", text="📨 Gönderiliyor...")
            self.progress_bar.set(0)
            
            thread = threading.Thread(target=self.send_bulk, args=(msg_template,))
            thread.daemon = True
            thread.start()
        
        def clear_log(self):
            self.log_text.delete("1.0", "end")
            self.log("Log temizlendi.")
        
        def log(self, msg):
            timestamp = datetime.now().strftime("%H:%M:%S")
            self.log_text.insert("end", f"[{timestamp}] {msg}\n")
            self.log_text.see("end")
            self.window.update_idletasks()
        
        def run(self):
            self.window.mainloop()
    
    if __name__ == "__main__":
        app = NetgsmSMSBot()
        app.run()
  • 26-03-2026, 00:09:27
    #2
    wisex adlı üyeden alıntı: mesajı görüntüle

    pip install customtkinter openpyxl pandas python-dotenv netgsm-sms
    import customtkinter as ctk
    from tkinter import filedialog, messagebox, scrolledtext
    import pandas as pd
    import threading
    import time
    import re
    from datetime import datetime
    import os
    from dotenv import load_dotenv, set_key
    from netgsm import Netgsm
    from netgsm.exceptions.api_exception import ApiException, NotAcceptableException
    from netgsm.utils.enums import Encoding
    
    ctk.set_appearance_mode("dark")
    ctk.set_default_color_theme("blue")
    
    ENV_FILE = ".env"
    
    class NetgsmSMSBot:
        def __init__(self):
            self.window = ctk.CTk()
            self.window.title("Netgsm Toplu SMS Botu")
            self.window.geometry("950x750")
            
            self.excel_file = None
            self.contacts = []
            self.is_sending = False
            self.netgsm_client = None
            
            load_dotenv(ENV_FILE)
            
            self.setup_ui()
            
        def setup_ui(self):
            self.tabview = ctk.CTkTabview(self.window, width=900, height=700)
            self.tabview.pack(padx=20, pady=20, fill="both", expand=True)
            
            self.tabview.add("📨 SMS Gönder")
            self.tabview.add("⚙️ Netgsm Ayarları")
            
            self.setup_send_tab()
            self.setup_settings_tab()
        
        def setup_send_tab(self):
            send_tab = self.tabview.tab("📨 SMS Gönder")
            
            excel_frame = ctk.CTkFrame(send_tab)
            excel_frame.pack(fill="x", padx=20, pady=(20,10))
            
            self.excel_label = ctk.CTkLabel(excel_frame, text="📁 Excel dosyası seçilmedi", font=("Roboto",12))
            self.excel_label.pack(side="left", padx=(0,10))
            
            select_btn = ctk.CTkButton(excel_frame, text="Excel Seç", command=self.select_excel, width=120)
            select_btn.pack(side="right")
            
            msg_frame = ctk.CTkFrame(send_tab)
            msg_frame.pack(fill="both", expand=True, padx=20, pady=10)
            
            ctk.CTkLabel(msg_frame, text="Mesaj Şablonu:", font=("Roboto",14, "bold")).pack(anchor="w")
            self.message_text = ctk.CTkTextbox(msg_frame, height=150)
            self.message_text.pack(fill="both", expand=True, pady=5)
            self.message_text.insert("1.0", "Merhaba ${isim},\nNetgsm ile toplu SMS gönderimi testidir.\nTeşekkürler.")
            
            tip_label = ctk.CTkLabel(msg_frame, text="💡 İpucu: ${isim} ve ${soyisim} değişkenlerini kullanabilirsiniz",
                                     font=("Roboto",10), text_color="gray")
            tip_label.pack(anchor="w")
            
            prog_frame = ctk.CTkFrame(send_tab)
            prog_frame.pack(fill="x", padx=20, pady=10)
            
            self.progress_bar = ctk.CTkProgressBar(prog_frame)
            self.progress_bar.pack(fill="x", pady=5)
            self.progress_bar.set(0)
            
            self.progress_label = ctk.CTkLabel(prog_frame, text="Hazır", font=("Roboto",12))
            self.progress_label.pack()
            
            log_frame = ctk.CTkFrame(send_tab)
            log_frame.pack(fill="both", expand=True, padx=20, pady=10)
            
            ctk.CTkLabel(log_frame, text="İşlem Günlüğü:", font=("Roboto",14, "bold")).pack(anchor="w")
            self.log_text = scrolledtext.ScrolledText(log_frame, height=12, bg="#2b2b2b", fg="white",
                                                      font=("Consolas",10))
            self.log_text.pack(fill="both", expand=True, pady=5)
            
            self.send_btn = ctk.CTkButton(send_tab, text="📨 SMS Göndermeyi Başlat",
                                          command=self.start_sending, height=45,
                                          font=("Roboto",14,"bold"))
            self.send_btn.pack(pady=(10,20), padx=20, fill="x")
        
        def setup_settings_tab(self):
            settings_tab = self.tabview.tab("⚙️ Netgsm Ayarları")
            
            info_frame = ctk.CTkFrame(settings_tab)
            info_frame.pack(fill="x", padx=20, pady=20)
            ctk.CTkLabel(info_frame, text="Netgsm Hesap Bilgileri", font=("Roboto",16,"bold")).pack(pady=5)
            ctk.CTkLabel(info_frame, text="Bu bilgiler .env dosyasında saklanacaktır.",
                         font=("Roboto",10), text_color="gray").pack()
            
            user_frame = ctk.CTkFrame(settings_tab)
            user_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(user_frame, text="Kullanıcı Adı:", width=120).pack(side="left", padx=5)
            self.username_entry = ctk.CTkEntry(user_frame, width=300)
            self.username_entry.pack(side="left", padx=5)
            self.username_entry.insert(0, os.getenv("NETGSM_USERNAME", ""))
            
            pass_frame = ctk.CTkFrame(settings_tab)
            pass_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(pass_frame, text="Şifre:", width=120).pack(side="left", padx=5)
            self.password_entry = ctk.CTkEntry(pass_frame, width=300, show="*")
            self.password_entry.pack(side="left", padx=5)
            self.password_entry.insert(0, os.getenv("NETGSM_PASSWORD", ""))
            
            header_frame = ctk.CTkFrame(settings_tab)
            header_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(header_frame, text="Gönderici Başlığı:", width=120).pack(side="left", padx=5)
            self.header_entry = ctk.CTkEntry(header_frame, width=300)
            self.header_entry.pack(side="left", padx=5)
            self.header_entry.insert(0, os.getenv("NETGSM_MSGHEADER", ""))
            
            test_frame = ctk.CTkFrame(settings_tab)
            test_frame.pack(fill="x", padx=20, pady=10)
            ctk.CTkLabel(test_frame, text="Test Numarası:", width=120).pack(side="left", padx=5)
            self.testno_entry = ctk.CTkEntry(test_frame, width=300)
            self.testno_entry.pack(side="left", padx=5)
            self.testno_entry.insert(0, os.getenv("NETGSM_TEST_NUMBER", ""))
            ctk.CTkLabel(test_frame, text="(10 haneli, 0'sız)", font=("Roboto",9), text_color="gray").pack(side="left", padx=5)
            
            btn_frame = ctk.CTkFrame(settings_tab)
            btn_frame.pack(fill="x", padx=20, pady=20)
            
            save_btn = ctk.CTkButton(btn_frame, text="💾 Bilgileri Kaydet", command=self.save_settings, width=150)
            save_btn.pack(side="left", padx=10)
            
            test_btn = ctk.CTkButton(btn_frame, text="🔍 Test SMS Gönder", command=self.test_sms, width=150, fg_color="green")
            test_btn.pack(side="left", padx=10)
            
            self.settings_status = ctk.CTkLabel(settings_tab, text="", font=("Roboto",11))
            self.settings_status.pack(pady=10)
        
        def select_excel(self):
            filename = filedialog.askopenfilename(filetypes=[("Excel files","*.xlsx *.xls")])
            if filename:
                self.excel_file = filename
                self.excel_label.configure(text=f"📄 {os.path.basename(filename)}")
                self.log(f"Excel dosyası seçildi: {filename}")
                try:
                    self.load_contacts()
                except Exception as e:
                    self.log(f"❌ Excel okuma hatası: {str(e)}")
        
        def load_contacts(self):
            df = pd.read_excel(self.excel_file, engine='openpyxl')
            self.contacts = []
            for _, row in df.iterrows():
                phone = str(row.get('telefon', row.get('phone', ''))).strip()
                phone = re.sub(r'\D', '', phone)
                if phone.startswith('0') and len(phone) == 11:
                    phone = phone[1:]
                if len(phone) == 10:
                    phone = phone
                else:
                    self.log(f"⚠️ Geçersiz numara formatı: {phone}")
                    continue
                self.contacts.append({
                    'phone': phone,
                    'first_name': str(row.get('isim', row.get('first_name', ''))).strip(),
                    'last_name': str(row.get('soyisim', row.get('last_name', ''))).strip()
                })
            self.log(f"✅ {len(self.contacts)} kişi yüklendi.")
        
        def save_settings(self):
            username = self.username_entry.get().strip()
            password = self.password_entry.get().strip()
            header = self.header_entry.get().strip()
            testno = self.testno_entry.get().strip()
            
            if not username or not password or not header:
                messagebox.showerror("Hata", "Kullanıcı adı, şifre ve gönderici başlığı zorunludur.")
                return
            
            set_key(ENV_FILE, "NETGSM_USERNAME", username)
            set_key(ENV_FILE, "NETGSM_PASSWORD", password)
            set_key(ENV_FILE, "NETGSM_MSGHEADER", header)
            if testno:
                set_key(ENV_FILE, "NETGSM_TEST_NUMBER", testno)
            
            load_dotenv(ENV_FILE, override=True)
            
            self.settings_status.configure(text="✅ Bilgiler başarıyla kaydedildi.", text_color="green")
            self.log("Netgsm ayarları kaydedildi.")
        
        def test_sms(self):
            self.save_settings()
            
            username = os.getenv("NETGSM_USERNAME")
            password = os.getenv("NETGSM_PASSWORD")
            header = os.getenv("NETGSM_MSGHEADER")
            testno = os.getenv("NETGSM_TEST_NUMBER")
            
            if not testno:
                messagebox.showerror("Hata", "Test numarası girilmemiş.")
                return
            
            testno = re.sub(r'\D', '', testno)
            if testno.startswith('0'):
                testno = testno[1:]
            if len(testno) != 10:
                messagebox.showerror("Hata", "Test numarası 10 haneli olmalı (başında 0 olmadan).")
                return
            
            try:
                client = Netgsm(username=username, password=password)
                message = {
                    "msg": "Bu bir test mesajıdır. Netgsm SDK çalışıyor.",
                    "no": testno
                }
                response = client.sms.send(msgheader=header, messages=[message], encoding=Encoding.TR)
                self.log(f"✅ Test SMS gönderildi! JobID: {response.get('jobid')}")
                messagebox.showinfo("Başarılı", f"Test SMS gönderildi.\nJobID: {response.get('jobid')}")
            except NotAcceptableException as e:
                self.log(f"❌ Netgsm hatası: {e.message} (Kod: {e.code})")
                messagebox.showerror("Netgsm Hatası", f"{e.message}\nKod: {e.code}")
            except ApiException as e:
                self.log(f"❌ API hatası: {e.message}")
                messagebox.showerror("API Hatası", e.message)
        
        def send_sms_via_sdk(self, phone, message):
            username = os.getenv("NETGSM_USERNAME")
            password = os.getenv("NETGSM_PASSWORD")
            header = os.getenv("NETGSM_MSGHEADER")
            
            if not all([username, password, header]):
                return False, "API bilgileri eksik. Ayarlar sekmesini kontrol edin."
            
            try:
                client = Netgsm(username=username, password=password)
                msg_obj = {
                    "msg": message,
                    "no": phone
                }
                response = client.sms.send(msgheader=header, messages=[msg_obj], encoding=Encoding.TR)
                if response.get('jobid'):
                    return True, f"JobID: {response.get('jobid')}"
                else:
                    return False, "Bilinmeyen yanıt"
            except NotAcceptableException as e:
                return False, f"{e.message} (Kod: {e.code})"
            except ApiException as e:
                return False, e.message
            except Exception as e:
                return False, str(e)
        
        def send_bulk(self, message_template):
            total = len(self.contacts)
            success = 0
            fail = 0
            
            for i, contact in enumerate(self.contacts):
                if not self.is_sending:
                    break
                
                msg = message_template
                msg = msg.replace('${isim}', contact['first_name'])
                msg = msg.replace('${soyisim}', contact['last_name'])
                
                ok, info = self.send_sms_via_sdk(contact['phone'], msg)
                if ok:
                    success += 1
                    self.log(f"✅ Gönderildi: {contact['phone']} - {contact['first_name']} ({info})")
                else:
                    fail += 1
                    self.log(f"❌ Başarısız: {contact['phone']} - {info}")
                
                progress = (i+1)/total
                self.progress_bar.set(progress)
                self.progress_label.configure(text=f"{i+1}/{total} - Başarılı: {success}, Başarısız: {fail}")
                time.sleep(0.5)
            
            self.log(f"\n{'='*50}\n📊 RAPOR\nToplam: {total}\nBaşarılı: {success}\nBaşarısız: {fail}\n{'='*50}")
            messagebox.showinfo("Tamamlandı", f"Gönderim bitti.\nBaşarılı: {success}\nBaşarısız: {fail}")
        
        def start_sending(self):
            if self.is_sending:
                messagebox.showwarning("Uyarı", "Zaten gönderim devam ediyor.")
                return
            if not self.excel_file:
                messagebox.showwarning("Uyarı", "Önce bir Excel dosyası seçin.")
                return
            if not self.contacts:
                self.load_contacts()
                if not self.contacts:
                    messagebox.showerror("Hata", "Excel'de geçerli numara bulunamadı.")
                    return
            
            msg_template = self.message_text.get("1.0", "end-1c").strip()
            if not msg_template:
                messagebox.showwarning("Uyarı", "Mesaj şablonu boş olamaz.")
                return
            
            if not all([os.getenv("NETGSM_USERNAME"), os.getenv("NETGSM_PASSWORD"), os.getenv("NETGSM_MSGHEADER")]):
                messagebox.showwarning("Uyarı", "Netgsm API bilgileri eksik. Lütfen ayarlar sekmesinden girin.")
                self.tabview.set("⚙️ Netgsm Ayarları")
                return
            
            self.is_sending = True
            self.send_btn.configure(state="disabled", text="📨 Gönderiliyor...")
            self.progress_bar.set(0)
            
            thread = threading.Thread(target=self.send_bulk, args=(msg_template,))
            thread.daemon = True
            thread.start()
        
        def clear_log(self):
            self.log_text.delete("1.0", "end")
            self.log("Log temizlendi.")
        
        def log(self, msg):
            timestamp = datetime.now().strftime("%H:%M:%S")
            self.log_text.insert("end", f"[{timestamp}] {msg}\n")
            self.log_text.see("end")
            self.window.update_idletasks()
        
        def run(self):
            self.window.mainloop()
    
    if __name__ == "__main__":
        app = NetgsmSMSBot()
        app.run()
    Pm bakarmısınız