YENİ 📢 Sınırlı lansman teklifi: Tüm oyunlarda ilk aya %70'e varan indirim!Paketleri görün

python-telegram-bot botunda MySQL veritabanı kullanma (aiomysql)

Telegram botlarıUpdated Nov 6, 202611 min read

Kısa cevap. python-telegram-bot botunuza aiomysql ekleyin, autocommit=True ile tek bir havuzu post_init içinde açın, her handler erişebilsin diye havuzu application.bot_data içinde tutun ve post_shutdown içinde kapatın. %s yer tutucularını kullanın ve Telegram ID'lerini BIGINT olarak saklayın, INT olarak değil. PlayNHost'ta her bot planı bir MySQL veritabanı içerir: veritabanını Veritabanları sayfasında oluşturun, bilgilerini Ortam bölmesine yapıştırın, aşağıdaki kod olduğu gibi çalışır. Veritabanı, PlayNHost sunucularınızdan ve phpMyAdmin'den gelen bağlantıları kabul eder, kendi bilgisayarınızdan gelenleri kabul etmez. Bu kodu 6 Kasım 2026 tarihinde python-telegram-bot 22.8, aiomysql 0.3.2 ve Python 3.12 ile, bizimki gibi ayarlanmış bir veritabanı hesabıyla (20 bağlantı, 120 saniyelik sorgu sınırı) MariaDB 10.11 üzerinde ve Telegram'ın Bot API sunucusunun bir benzeriyle test ettik: 8 komut girdi, 8 yanıt çıktı, notlar veritabanına kaydedildi. Gerçek bir Telegram girişi teste dahil değildi.

Neye ihtiyacınız var

1. Veritabanını oluşturun

  1. Bot sunucunuzu panelde açın ve Veritabanları öğesine tıklayın.
  2. Yeni veritabanı öğesine tıklayın. Etiket isteğe bağlıdır; Şu adla oluşturulacak: alanı tam adı gösterir. Veritabanı oluştur öğesine tıklayın.
  3. Yeni veritabanının sayfası açılır (sonradan Veritabanlarınız içinden ona tıklayabilirsiniz). Bağlantı kartı Sunucu adresi, Port, Veritabanı, Kullanıcı Adı ve Şifre bilgilerini gösterir. Hazır yapılandırma altında, Şunun için yapılandırma alanında aynı değerlerle doldurulmuş bir Python (aiomysql) parçacığı bulunur.

2. Bilgileri Ortam bölmesine girin

  1. Bot sayfasını, Ortam bölmesini açın ve .env dosyası yapıştır öğesine tıklayın.
  2. Bağlantı kartındaki değerlerle şu beş satırı yapıştırın:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD satırı bir Gizli rozeti alır: yalnızca yazılabilir olarak saklanır ve bir daha gösterilmez. 5 değişkeni içe aktar öğesine tıklayın.

Bağlantı kartındaki gerçek portu kullanın. Bot token'ı bu satırlardan biri değildir: Bot sayfasında, Telegram bölmesinde kaydettiğiniz token koda zaten TELEGRAM_BOT_TOKEN adıyla ulaşır, aşağıdaki kodun okuduğu ad budur (ayrıca BOT_TOKEN, TOKEN, TELEGRAM_TOKEN ve TELOXIDE_TOKEN adlarıyla da). .env dosyanızda bir token satırı varsa, iletişim kutusu onu atlar. Bu bölme hakkında daha fazlası: Discord botunuz için ortam değişkenleri ve gizli değerler nasıl ayarlanır.

3. Kod

Küçük bir not botu: /note buy milk bir not kaydeder, /notes son beş notunuzu gösterir.

requirements.txt

python-telegram-bot>=22,<23
aiomysql

bot.py

import os

import aiomysql
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes


async def post_init(application: Application) -> None:
    # One pool for the whole bot. The values come from the Environment pane.
    pool = await aiomysql.create_pool(
        host=os.environ["DB_HOST"],
        port=int(os.environ["DB_PORT"]),
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASSWORD"],
        db=os.environ["DB_NAME"],
        autocommit=True,
        maxsize=5,
    )
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "CREATE TABLE IF NOT EXISTS notes ("
                "id INT AUTO_INCREMENT PRIMARY KEY, "
                "user_id BIGINT NOT NULL, "
                "text VARCHAR(500) NOT NULL, "
                "created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
                "KEY user_id (user_id))"
            )
    application.bot_data["pool"] = pool
    print("Database ready")
    print(f"Logged in as @{application.bot.username}")


async def post_shutdown(application: Application) -> None:
    pool = application.bot_data.get("pool")
    if pool is not None:
        pool.close()
        await pool.wait_closed()


async def note(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    text = " ".join(context.args).strip()
    if not text:
        await update.effective_message.reply_text("Write your note after the command, for example: /note buy milk")
        return
    if len(text) > 500:
        await update.effective_message.reply_text("A note can be up to 500 characters.")
        return
    pool = context.bot_data["pool"]
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "INSERT INTO notes (user_id, text) VALUES (%s, %s)",
                (update.effective_user.id, text),
            )
    await update.effective_message.reply_text("Saved. Send /notes to see your last five.")


async def notes(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    pool = context.bot_data["pool"]
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "SELECT text FROM notes WHERE user_id = %s ORDER BY id DESC LIMIT 5",
                (update.effective_user.id,),
            )
            rows = await cur.fetchall()
    if not rows:
        await update.effective_message.reply_text("You have no notes yet.")
        return
    await update.effective_message.reply_text("\n".join(f"- {text}" for (text,) in rows))


def main() -> None:
    application = (
        Application.builder()
        .token(os.environ["TELEGRAM_BOT_TOKEN"])
        .post_init(post_init)
        .post_shutdown(post_shutdown)
        .build()
    )
    application.add_handler(CommandHandler("note", note))
    application.add_handler(CommandHandler("notes", notes))
    application.run_polling()


if __name__ == "__main__":
    main()

Ne yapar: run_polling() önce Telegram'a botun kim olduğunu sorar, sonra post_init işlevini çalıştırır ve ancak ondan sonra mesaj almaya başlar. post_init havuzu açar, yoksa notes tablosunu oluşturur, havuzu application.bot_data["pool"] içine koyar ve Database ready ile Logged in as @your_bot satırlarını yazdırır. Her handler aynı sözlüğü context.bot_data olarak alır. Bot durduğunda run_polling(), havuzu kapatan post_shutdown işlevini çalıştırır.

context.args, komuttan sonraki metindir ve kelimelere bölünmüştür; bu yüzden kod onu tek boşluklarla yeniden birleştirir: bir notun içindeki satır sonları ve çift boşluklar tek boşluğa dönüşür. Notu tam olarak yazıldığı gibi istiyorsanız update.effective_message.text değerini alın ve komutu kendiniz kesin.

%s yer tutucularını sürücü doldurur, bu yüzden '); DROP TABLE notes; -- gibi bir not düz metin olarak saklanır. Testimizde 2. satır olarak kaydedildi ve tablo yerinde kaldı. SQL'i asla f-string ile oluşturmayın.

Telegram ID'lerini BIGINT olarak saklayın

Telegram'ın Bot API belgeleri, bir kullanıcı veya sohbet ID'sinin “32'den fazla anlamlı bit içerebileceğini” ama en fazla 52 bit olduğunu söyler; yani 64 bitlik bir tamsayı güvenlidir. ID'ler sayfası aralıkları verir: kullanıcı ID'leri 1.099.511.627.775'e kadar çıkar, grup, süper grup ve kanal ID'leri ise negatiftir. Süper gruplar ve kanallar -1.000.000.000.001 ile -1.997.852.516.352 arasındadır (örneğin -1001234567890). MySQL'in INT türü 2.147.483.647'de durur. Testimizde, veritabanı sunucumuzun kullandığı aynı katı SQL kipiyle, 7.000.000.001 kullanıcı ID'sini bir INT sütununa kaydetmek başarısız oldu. aiomysql şunu verdi:

pymysql.err.DataError: (1264, "Out of range value for column 'user_id' at row 1")

Bir BIGINT sütunu bunu sakladı, -1001234567890 değerini de. Sohbet ID'leri de sığsın diye düz BIGINT kullanın, BIGINT UNSIGNED değil.

4. Çalıştırın

  1. bot.py ve requirements.txt dosyalarını Dosyalar bölümünden yükleyin.
  2. Başlat düğmesine basın ve Konsol bölümünü açın. Başlangıçta requirements.txt içindeki paketler kurulur, ardından [PlayNHost] Starting your bot, Database ready ve Logged in as @your_bot görünür. Bu son satır, Genel bakış içindeki durumu Telegram’da çevrimiçi yapar.
  3. Telegram'da botunuza /note buy milk, ardından /notes gönderin. Satırlara bakmak için veritabanının sayfasını açın ve phpMyAdmin’de aç öğesine tıklayın.

İkinci başlatmadan itibaren konsol ayrıca Warning: Table 'notes' already exists satırını gösterir, önünde aiomysql'in cursors.py dosyasının yolu bulunur. Bu, CREATE TABLE IF NOT EXISTS için MariaDB'nin notudur ve sürücü tarafından iletilir. Bir sorun yok.

Testimizde Telegram'ın benzeri şu komutları gönderdi ve şu yanıtları aldı:

GönderilenYanıt
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note ve 501 karakterA note can be up to 500 characters.
/notesKaydedilen notlar, en yeni önce
/notes ikinci bir kullanıcıdanYou have no notes yet.

Her seferinde bir güncelleme

Varsayılan ayarlarla python-telegram-bot her seferinde bir güncellemeyi işler: sonraki komut, önceki handler bitene kadar bekler. Testimizde birlikte gelen 4 komut geliş sırasıyla yanıtlandı ve testten sonra botun 1 açık bağlantısı vardı. maxsize=5, concurrent_updates seçeneğini Application.builder() içinde açarsanız ya da işler ve handler'lar aynı anda sorgu yaparsa yine de önemlidir: havuz asla 5'ten fazla bağlantı açmaz ve fazladan bir sorgu boş bir bağlantıyı bekler.

Autocommit tuzağı

aiomysql bağlantıları autocommit kapalı olarak açar. Bu durumda bir INSERT açık bir işlemin içinde kalır. Bağlantı, bu işlem hâlâ açıkken havuza döndüğünde aiomysql onu kapatır ve MariaDB onaylanmamış satırı atar. Testimizde, autocommit=True olmayan bir havuzda eklenen satırı 0 başka bağlantı gördü. Ya yukarıdaki gibi autocommit=True değerini create_pool işlevine verin ya da yazma işlemlerinizden sonra await conn.commit() çağırın.

Başarısız bir sorgu botu durdurmaz

Testimizde bot çalışırken tabloyu sildik ve /notes gönderdik. python-telegram-bot No error handlers are registered, logging exception. yazdırdı ve pymysql.err.ProgrammingError: (1146, "Table '...notes' doesn't exist") ile biten bir geri izleme verdi, o komuta yanıt göndermedi ve çalışmaya devam etti: tablo geri gelince bir sonraki /note kaydedildi. Komutu gönderen kişi hiçbir şey görmez, bu yüzden hatayı handler içinde yakalayın ya da application.add_error_handler() ile bir hata handler'ı kaydedin ve kısa bir mesajla yanıt verin.

Sakin bir gece havuzu bozmaz

Veritabanı sunucumuz 8 saat boşta kalan bir bağlantıyı kapatır. O kadar uzun süre mesaj almayan bir bot, örneğin gece boyunca, havuzdaki bağlantılarının kapandığını görür. aiomysql, bir handler bağlantı istediğinde bunu kontrol eder ve kapalı olanın yerine yenisini açar. Boşta kalma sınırını 5 saniyeye indirerek test ettik: 7 sakin saniyeden sonra bir sonraki /note hatasız kaydedildi.

Havuzunuz için önemli sınırlar

  • Veritabanı kullanıcısı başına aynı anda 20 bağlantı. Aynı girişi kullanan her işlem sayılır. 21. bağlantı şu hatayla reddedilir: (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)"). maxsize=5 botu bunun çok altında tutar.
  • 120 saniyeden uzun süren bir sorgu durdurulur. MariaDB onu 1969 hatasıyla sonlandırır, Query execution was interrupted (max_statement_time exceeded). Daha önceki bir testimizde, aynı şekilde ayarlanmış bir hesapta bu, 120,03 saniye sonra oldu. Bir botun sorguları normalde milisaniyeler sürer, bu yüzden bu yalnızca başıboş kalmış bir sorguyu etkiler.
  • Veritabanı başına 500 MB. Veritabanları sayfası ne kadarının kullanıldığını gösterir. 500 MB'a ulaşınca, siz satırları silene kadar yazma işlemleri durdurulur.
  • Sunucu yedekleri veritabanını içermez. Zaman zaman phpMyAdmin'den dışa aktarın.

Hatalar ve anlamları

Bunlar, aiomysql'in testimizde verdiği hatalardır ve konsolda bir geri izlemeyi bitirdikleri biçimde yazılmıştır (aiomysql'in altta kullandığı PyMySQL'den gelirler):

  • pymysql.err.OperationalError: (1045, "Access denied for user ..."): yanlış DB_USER veya DB_PASSWORD. Bağlantı kartında Şifreyi sıfırla işleminden sonra eski şifre hemen çalışmaz: DB_PASSWORD değerini değiştirin ve botu yeniden başlatın.
  • pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on ..."): yanlış sunucu adresi veya port ya da PlayNHost dışından gelen bir bağlantı. Veritabanı yalnızca PlayNHost sunucularından ve phpMyAdmin'den gelen bağlantıları kabul eder, bu yüzden kendi bilgisayarınızda da bunu alırsınız.
  • pymysql.err.OperationalError: (1226, ... 'max_user_connections' ...): çok fazla açık bağlantı, yukarıdaki sınırlara bakın. Örneğin botun ikinci bir kopyası ya da her mesaj için açılan bir havuz.
  • pymysql.err.DataError: (1264, "Out of range value for column 'user_id' at row 1"): bir ID sütunu INT türünde. Onu BIGINT yapın.
  • pymysql.err.ProgrammingError: (1146, "Table '...' doesn't exist"): tablo yok. Yukarıdaki kod onu başlangıçta oluşturur; elle oluşturduysanız adını kontrol edin.

Ortam bölmesinde eksik bir değişken, botu bağlanmadan önce durdurur; hata KeyError: 'DB_HOST' ya da eksik olanın adıdır, çünkü kod her değeri os.environ[...] ile okur. Değişkeni ekleyin, sonra botu yeniden başlatın: değişkenler bir sonraki başlatmada uygulanır.

Yanlış bir token botu veritabanı kısmından önce durdurur, çünkü post_init yalnızca Telegram token'ı kabul ettikten sonra çalışır. Sahte token'ımızla gerçek Telegram'a karşı bot hiçbir zaman Database ready yazdırmadı ve yaklaşık 1 saniye sonra şu hatayla durdu: telegram.error.InvalidToken: The token `...` was rejected by the server. Bu mesaj token'ın tamamını içerir, bu yüzden onu içeren konsol çıktısını herkese açık hiçbir yere yapıştırmayın. Token'ı @BotFather'dan yeniden kopyalayın ve Bot sayfasında, Telegram bölmesinde yapıştırın. Daha fazla Telegram hatası: Telegram botu 409 Conflict hatasını düzeltin.

Ne kadar bellek kullanır

Testimizde çalışan bot, 8 komutu yanıtladıktan sonra, havuzu açıkken, en küçük bot planımız gibi 512 MB ile sınırlı bir konteynerde 48,5 MB bellek (resident) kullandı. Daha önceki barındırma testimizde python-telegram-bot tek başına 44 MB ölçüldü (o bot, hiçbir mesajı işlemeden reddedilen bir token'da durdu), dolayısıyla yaklaşık 4,5 MB'lık fark aiomysql'i, havuzu ve işlenen komutları kapsar. python-telegram-bot ve aiomysql'i pip ile sıfırdan kurmak, dosya önbelleği dahil 73 MB'a çıktı. Kütüphaneler yan yana: PlayNHost’ta python-telegram-bot ve aiogram karşılaştırması.

Başka bir kütüphaneyle aynısı

Veritabanı dahil, Telegram botunuz için bir sunucuya mı ihtiyacınız var? Telegram bot planlarına göz atın.

Botun başka bir yerde mi? Bize taşı

EN YENİ BLOG YAZILARIMIZ