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