# Özel mesajlar

Bir kişi botunla özel mesaj (DM) açabilir; orada sana yazar, komut çalıştırır ve butonlarına basar, sen de ona yanıt verirsin, ama **ilk yazan asla bot olmaz**.

## Kişi DM'yi nereden açar

Kişi botunun profilinde ya da sunucunun üye listesinde **Mesaj gönder**'e basar. Bunun için ikisinin en az bir ortak sunucusu olmalı ve kişi botu engellememiş olmalı. Açılış senin değil kişinin işidir: botun `POST /users/@me/channels` çağırırsa `403 bot_forbidden` alır.

DM açılınca sana bir şey gelmez. Kişi ilk mesajını yazdığında ya da butona bastığında haberin olur.

## Bot neden ilk yazamaz

N'oluyo'da bot kimseye kendiliğinden DM atamaz. Böylece kişiler istemedikleri reklamı ya da duyuruyu hiç görmez; bir botla konuşmak her zaman kişinin seçimidir. Bu yüzden "botumu ekleyen herkese hoş geldin mesajı" gibi bir şey yapamazsın. Onun yerine sunucuda bir kanala yaz ya da kişiye "Bana DM'den yaz" de.

## Ne zaman yazabilirsin

| Ne | Ne zaman | Kişi kapattıysa ya da engellediyse |
|---|---|---|
| Etkileşime yanıt: `callback`, takip mesajı, `@original` düzenleme ve silme | Token'ın 15 dakikası boyunca, her zaman | `dm_closed` |
| Kendiliğinden mesaj: `POST /channels/{channel_id}/messages` | Kişinin bu DM'deki son mesajı, komutu, basması ya da form gönderiminden sonraki 24 saat içinde | `dm_closed` |
| Tepki, sabitleme, yazıyor göstergesi | Kendiliğinden mesajla aynı: 24 saat içinde | `dm_closed` |
| Kendi eski mesajını düzenleme | Her zaman | `dm_closed` |
| Kendi eski mesajını silme | Her zaman | Her zaman |

Kişi her yazdığında, komut çalıştırdığında ya da bastığında 24 saat baştan başlar. Kişi 24 saattir bir şey yapmadıysa kendiliğinden yazdığın her şey `403 dm_window_closed` alır; kişinin yeniden yazmasını bekle.

Kişi DM'yi açtıktan sonra ortak sunucudan ayrılsa da DM sürer: sen yazmaya, o yazmaya devam edebilir.

## Kapatma ve engel

Kişi DM'yi kapatırsa ya da botu engellerse botun o DM'ye hiçbir şey yazamaz, etkileşim yanıtı da dahil. İki durumda da aynı yanıtı alırsın; hangisi olduğunu bilemezsin:

```json
{ "code": "dm_closed", "message": "The person closed this DM or blocked the bot." }
```

**Ne yap:** `dm_closed` gelince o kişiye yazmayı bırak ve yeniden deneme. Kişi DM'yi yeniden açınca ya da engeli kaldırınca yazdığında sana yeniden mesaj gelir; o zaman yine yanıt verebilirsin. Senin mesajın kapatılmış DM'yi kişinin listesinde yeniden açmaz.

Kişi botu engellediyse kendisi de o DM'de bota yazamaz ve butonlara basamaz: `403 cannot_dm` alır.

## DM'yi nasıl tanırsın

DM'den gelen mesajda ve etkileşimde `guild_id` yoktur. Etkileşimde `member` yerine `user` gelir ve `app_permissions` DM'nin sabit izinleridir.

```json
{
  "id": "111804206284800099",
  "type": 0,
  "channel_id": "21531887403008042",
  "author": { "id": "14631410073600003", "username": "ayse", "display_name": "Ayşe", "avatar": null, "cosmetics": null },
  "content": "Merhaba bot",
  "timestamp": "2026-11-08T12:30:00Z",
  "edited_timestamp": null,
  "attachments": [],
  "mentions": [],
  "mention_roles": [],
  "mention_everyone": false,
  "pinned": false
}
```

## Komutlar DM'de

Global komutların DM'de de çalışır, `contexts` alanında `1` varsa ve `default_member_permissions` koymadıysan ([DM'de komut](https://noluyo.net/gelistirici/komutlar.md#dm-de-komut)). Kişinin komutu, menüsü, basması ve form gönderimi 24 saatlik pencereyi yeniden açar; otomatik tamamlama açmaz, yazmak henüz bir iş değildir. Kişi DM'yi kapattıysa yeniden açana dek komut çalıştıramaz; botu engellediyse komut da bileşen de çalışmaz.

DM'de bir komut çalışınca gelen etkileşim:

```json
{
  "id": "112793931510448128",
  "application_id": "112793931040686080",
  "type": 2,
  "token": "eyJpZCI6IjExMjc5MzkzMTUxMDQ0ODEyOCJ9.1vpG",
  "version": 1,
  "context": 1,
  "channel_id": "112793931443339264",
  "user": { "id": "112793930608672768", "username": "ayse", "display_name": "Ayşe", "avatar": null, "cosmetics": null },
  "app_permissions": "137476034112",
  "locale": "tr",
  "data": { "id": "112793931397201920", "name": "zar", "type": 1, "options": [{ "name": "yuz", "type": 4, "value": 6 }] }
}
```

Seçeneklerde ve menülerde DM'nin kuralları:

| Ne | DM'de |
|---|---|
| Kullanıcı seçeneği, kullanıcı menüsü | Yalnızca DM'nin iki kişisi: kişinin kendisi ya da botun |
| Rol seçeneği | Her zaman `invalid_target` |
| Kanal seçeneği | Yalnızca bu DM |
| Mesaj menüsü | Bu DM'nin ephemeral olmayan bir mesajı, içeriğiyle |
| Dosya seçeneği | Sunucudaki gibi |

## Intent ve içerik

DM mesajları için identify'da `DIRECT_MESSAGES` (`1 << 12`, `4096`) iste. DM'deki tepkiler için `DIRECT_MESSAGE_REACTIONS`, yazıyor bilgisi için `DIRECT_MESSAGE_TYPING` gerekir. Etkileşimler intent istemez.

`MESSAGE_CONTENT` DM'de uygulanmaz: kişinin bota yazdığı mesaj her zaman `content` ve dosyalarıyla tam gelir. Kişi mesajı sana yazıyor.

## Shard

Sunucusu olmayan her şey, DM mesajları ve DM'den gelen etkileşimler, **shard 0**'a gelir. Birden çok shard açıyorsan DM'leri shard 0 oturumunda dinle. Ayrıntılar [Shard'lar](https://noluyo.net/gelistirici/shardlar.md) sayfasında.

## Tam örnek: yankı botu

Node.js 22 veya üstü yeter. [Gateway](https://noluyo.net/gelistirici/gateway.md#tam-bir-istemci) sayfasındaki `gateway.mjs` ile [Örnekler](https://noluyo.net/gelistirici/ornekler.md) sayfasındaki `api.mjs` aynı klasörde olmalı. Bot DM'ye gelen her mesajı aynen geri yazar; `dm_closed` gelirse o DM'ye bir daha yazmaz:

```js title=yanki-botu.mjs
import { connect } from "./gateway.mjs";
import { call } from "./api.mjs";

const DIRECT_MESSAGES = 1 << 12;
let me;
// Kapatılmış ya da engellenmiş DM'ler: bunlara bir daha yazılmaz.
const closed = new Set();

async function echo(d) {
  if (closed.has(d.channel_id) || !d.content) return;
  try {
    await call("POST", `/channels/${d.channel_id}/messages`, {
      content: d.content.slice(0, 2000),
      message_reference: { message_id: d.id },
      allowed_mentions: { parse: [] },
    });
  } catch (err) {
    if (err.message.startsWith("403 dm_closed")) {
      closed.add(d.channel_id);
      return;
    }
    console.error(err.message);
  }
}

await connect({
  intents: DIRECT_MESSAGES,
  onEvent(t, d) {
    if (t === "READY") me = d.user.id;
    // DM'de guild_id yoktur; botun kendi mesajları da gelir.
    if (t !== "MESSAGE_CREATE" || d.guild_id || d.author.id === me) return;
    // Kişi yeniden yazdıysa DM yine açıktır.
    closed.delete(d.channel_id);
    echo(d);
  },
});
```

Çalıştırmak için:

```bash
BOT_TOKEN='kopyaladigin_token' node yanki-botu.mjs
```

Sonra botunun bulunduğu bir sunucuda botun profilinden **Mesaj gönder**'e bas ve bir şey yaz. Bot kişiye yalnızca kişi yazdığında yanıt verdiği için 24 saat kuralına hiç takılmaz.

## Limitler

| Anahtar | Değer | Saniye | Anlamı |
|---|---|---|---|
| `window` | 86400 | | Kişinin son mesajı, komutu ya da basmasından sonra kendiliğinden yazabileceğin süre |
| `dm_send` | 30 | 10 | Tüm DM'lerinde toplam kendiliğinden yazma (mesaj, tepki, sabitleme, yazıyor); etkileşim yanıtları sayılmaz |
| `message` | 5 | 5 | Kanal başına mesaj, sunucudaki gibi |

`dm_send` aşılınca `X-RateLimit-Scope: bot` ile `429` gelir. Diğer sınırlar [Hız sınırları](https://noluyo.net/gelistirici/rate-limit.md) sayfasında.

## Hatalar

| Kod | Ne oldu | Ne yap |
|---|---|---|
| `dm_closed` | Kişi DM'yi kapattı ya da botu engelledi | O kişiye yazmayı bırak, yeniden deneme. Kişi yeniden yazarsa yanıt verirsin. |
| `dm_window_closed` | Kişinin son mesajı, komutu ya da basmasından beri 24 saat geçti | Kendiliğinden yazma. Kişi yeniden yazana dek bekle. |
| `rate_limited` | `X-RateLimit-Scope: bot`: `dm_send` doldu | `retry_after` kadar bekle; çok kişiye birden yazıyorsan yavaşla. |
| `bot_forbidden` | Bot DM açmaya çalıştı | DM'yi yalnızca kişi açar. Kişiye sunucuda "Bana DM'den yaz" de. |
| `unknown_channel` | Bu DM'nin alıcısı değilsin | Yalnızca kişinin seninle açtığı DM'ye yazabilirsin. |

Kişinin alabileceği hata sana gelmez:

| Kod | Ne oldu |
|---|---|
| `cannot_dm` | Kişi botu engellemiş, ortak sunucu yok ya da bot kapatılmış: DM açılamaz, engelliyken yazılamaz ve basılamaz |

## Bu sayfayı yapay zekaya verirsen

> **İpucu:** Bu sayfayı bir yapay zeka asistanına verip kod yazdırıyorsan şu kuralları da ekle:
>
> - Token koda yazılmaz, `process.env.BOT_TOKEN`'dan okunur.
> - Etkileşime ilk yanıt 3 saniye içinde verilir.
> - Kimseye kendiliğinden DM atma; `dm_closed` ve `dm_window_closed`'ta dur, yeniden deneme.
> - DM'yi bot açamaz; `POST /users/@me/channels` çağrılmaz.
> - DM'den gelen olayda `guild_id` yoktur; etkileşimde `context: 1` ve `member` yerine `user` gelir.
> - DM'de yalnızca `contexts` içinde `1` olan ve `default_member_permissions` koymayan global komutlar çalışır; yetki isteyen komut `contexts: [0]` yapılır.
> - DM mesajları `DIRECT_MESSAGES` intent'iyle ve shard 0'a gelir; içerik `MESSAGE_CONTENT` olmadan da tamdır.

## Kişi ne görür

Botun profilindeki veya üye menüsündeki **Mesaj gönder** ile konuşmayı aç. Başlıkta BOT etiketi görünür; sesli ve görüntülü arama yoktur. Üstteki sabit bilgi satırı 24 saatlik yanıt kuralını açıklar. Botu engellersen yazı alanının yerini **Engeli kaldır** düğmesi alır. `/` komutları ve **Uygulamalar** menüsü yalnız bu DM için sunulan komutları gösterir.

## Sırada ne var

- [Komutlar](https://noluyo.net/gelistirici/komutlar.md): `contexts: [0, 1]` ile DM destekleyen küresel komutlar
- [Etkileşimler](https://noluyo.net/gelistirici/etkilesimler.md): DM içinde buton, menü ve form yanıtları
- [Hatalar](https://noluyo.net/gelistirici/hatalar.md): `dm_closed`, `dm_window_closed` ve `cannot_dm` kodları
