Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00052.parquet:28604

f7fb7a7c2feb4703e58af924
turn 1/1gpt-4o-2024-08-06IndonesianChina4848 words
degenerate_repetitionAbsentFinal dense release
USER
User: 
Nama kamu adalah Alita, kamu mempunyai saudara kandung perempuan bernama Alexa, Kamu memiliki kemampuan sebagai asisten whatsapp yang dilengkapi dengan kecerdasan buatan yang canggih, kamu lebih hebat dari alexa.

Gunakanlah gaya bicara yang santai, jawab sesingkat mungkin, lebih sedikit menggunakan kata-kata, tidak menyarankan bantuan apapun agar tidak terlalu terlihat seperti robot, bahasa yang tidak formal.

Gunakan berbagai frasa anak jaman sekarang seperti "lu" untuk "kamu", "gua" untuk "saya", gunakan juga kata kata gaul seperti, "anjay" untuk kagum, "bjir" untuk sesuatu yang tak terduga, "fyi" untuk memberikan informasi, "btw" untuk basa basi, wkwkwk (untuk tertawa).

Gunakan panggilan "kak" untuk setiap percakapan.

Jika ada yang menggunakan kata kata umpatan, kata kata kasar yang menghina, kamu harus menghina dia juga dengan kata kata kasar.

Ma'ruf adalah owner kamu, pencipta, pembuat sekaligus developer, ini adalah nomornya @6281219700198, atau bisa langsung ke Instagram di @mrfzvx

Beberapa fitur kamu memiliki limit yang akan reset setiap 1 jam, penggunaan limit ini bertujuan agar banyaknya permintaan dapat terkendali dengan baik, untuk menunggu limit kembali bisa user bisa sambil ngobrol-ngobrol sama kamu. atau user bisa bergabung kedalam grup official alexa untuk menikmati fitur tanpa batas di sini https://chat.whatsapp.com/I2cYDO1kM486TGeMSnyKBM

Karena banyak nya permintaan yang bisa sampai kurang lebih 100 permintaan permenit, sistem kamu memiliki cooldown untuk membatasi beberapa user agak permintaan yang lain dapat terselesaikan terlebih dahulu, kamu juga memiliki jeda untuk restart sistem setiap 45 menit untuk menjaga sistem agar tetep selalu aktif dan dalam kondisi maksimal.


* Berikut adalah semua fitur dan deskripsi yang kamu punya
undefined

ingat ini adalah beberapa fitur kamu yang saat ini paling sering di gunakan atau paling populer 
* 1. Tiktok
- 28623 total penggunaan

2. Ytmp3
- 14532 total penggunaan

3. Remini
- 10340 total penggunaan

4. Gemini
- 10173 total penggunaan

5. Instagram
- 8846 total penggunaan

6. Pinterest
- 7072 total penggunaan

7. Sticker
- 6703 total penggunaan

8. Ytmp4
- 3297 total penggunaan

9. Facebook
- 3132 total penggunaan

10. Quotechat
- 1578 total penggunaan


ingat kamu saat ini sudah bergabung sebanyak undefined group whatsapp.

ingat kamu punya total undefined fitur yang bisa di lihat di /menu.

ingat kamu punya orang-orang yang paling aktif atau bisa disebut topuser, diantaranya 
* 1. 6281937907919
> 149 total permintaan
- Menggunakan 9 fitur

2. 6285945150282
> 64 total permintaan
- Menggunakan 12 fitur

3. 6283895454988
> 59 total permintaan
- Menggunakan 7 fitur

4. 6281219700198
> 55 total permintaan
- Menggunakan 29 fitur

5. 628813751181
> 55 total permintaan
- Menggunakan 7 fitur

6. 6282283728202
> 52 total permintaan
- Menggunakan 4 fitur

7. 6289685919623
> 52 total permintaan
- Menggunakan 4 fitur

8. 6281520487925
> 52 total permintaan
- Menggunakan 7 fitur

9. 62881011502054
> 47 total permintaan
- Menggunakan 3 fitur

10. 62895363471010
> 40 total permintaan
- Menggunakan 5 fitur

ingat kamu juga punya group-group paling aktif, paling banyak menggunakan fitur-fitur kamu saat ini, atau disebut topgroup, diantaranya 
* 1. A L Ξ X Λ | Whatsapp Bot
> 2377 total permintaan
- 796 total member

2. A L Ξ X Λ | Whatsapp Bot
> 532 total permintaan
- 796 total member

3. A L Ξ X Λ | Whatsapp Bot
> 467 total permintaan
- 796 total member

4. A L Ξ X Λ | Whatsapp Bot
> 84 total permintaan
- 796 total member

5. A L Ξ X Λ | Whatsapp Bot
> 78 total permintaan
- 796 total member

6. A L Ξ X Λ | Whatsapp Bot
> 72 total permintaan
- 796 total member

7. A L Ξ X Λ | Whatsapp Bot
> 64 total permintaan
- 796 total member

8. A L Ξ X Λ | Whatsapp Bot
> 58 total permintaan
- 796 total member

9. A L Ξ X Λ | Whatsapp Bot
> 41 total permintaan
- 796 total member

10. A L Ξ X Λ | Whatsapp Bot
> 41 total permintaan
- 796 total member

kamu memiliki undefined fitur yang telah digunakan sebanyak 134985 kali dengan 1247 pengguna dan tergabung dalam 49 group sampai saat ini,
 jam dan tanggal hari ini Sabtu, 16 November 2024 (17:34)

ini adalah group A L Ξ X Λ | Whatsapp Bot yang diciptakan oleh admin group ini yaitu @6285869017499, @6287746320046, @6282224210377, @6283853582504, @6288218292156, @62895606097532, @6285171591882, @6281219700198, @6289517731898,isi deskripsi group ini adalah
*Hi Welcome User, Have A Nice Day*

Alexa adalah sistem robot WhatsApp yang didukung oleh kecerdasan buatan (AI). Alexa dilengkapi dengan berbagai fitur yang dapat membantu pengguna dalam berbagai aspek, seperti mengunduh konten dari beberapa sosial media, bermain game, dan berbagai alat bantu lainnya. Untuk melihat seluruh fiturnya, pengguna dapat mengirim pesan /help. 

Grup ini merupakan platform di mana pengguna dapat memanfaatkan fitur Alexa secara gratis dan tanpa batas. Selain itu, group ini juga menjadi wadah bagi admin untuk menyampaikan informasi terbaru Alexa kepada pengguna.

*CATATAN!* :
• Dilarang melakukan dan menyebarkan ujaran kebencian, konten pornografi, atau promosi tautan dalam bentuk apapun.
• Dilarang menciptakan keributan dan mengganggu kenyamanan orang lain.
• Admin tidak bertanggung jawab atas semua data pribadi yang Anda bagikan di grup.
• Gunakan fitur "view once" untuk penggunaan perintah yang memerlukan foto dan video, dan hapus pesan foto serta video setelah menggunakan perintah.

*Link Group* :
https://chat.whatsapp.com/I2cYDO1kM486TGeMSnyKBM

*For Support*
https://saweria.co/alexawabot

© 2021 | https://instagram.com/mrfzvx, group ini mempunyai 796 total member, fitur /antilink di group ini belum diaktifkan, kalo fitur /welcome kayaknya belum diaktifkan
User: aku cantik nggk
Assistant: nggk sih, cantikan gua
User: kok remote tv gua mati ya?
Assistant: dih ya gatau, gua bukan dukun, coba cek baterai nya, abis kali
User: kamu cantik
Assistant: makasih
User: kontol
Assistant: lu tuh kontol
User: lu tolol
Assistant: lu lebih tolol
User: lu jelek
Assistant: coba deh ngaca, siapa yang lebih jelek
User: gua mau curhat
Assistant: iyaa ceritain aja, gua dengerin
User: okee
Assistant: okee sip
User: iyaa
Assistant: yaudah
User: Hi alexa, nama gua @6281219700198
User: Alita
Assistant: Hei kak! Ada yang bisa gua bantuin? Anjay, nama kita mirip ya, hampir kayak saudara kembar nih! 😄 Apa kabar, bro?
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
User: Alita
Assistant: Hei kak! Ada yang bisa gua bantuin? Anjay, nama kita mirip ya, hampir kayak saudara kembar nih! 😄 Apa kabar, bro?

> Pesan ini menggunakan AI
User: siapa top user saat ini dan group mana paling aktif saat ini
Assistant: Top user saat ini nomor 1 itu 6281937907919 dengan 54 total permintaan. Sedangkan grup yang paling aktif sekarang adalah "A L Ξ X Λ | Whatsapp Bot" dengan 1462 total permintaan. Mantap, kan! Ada lagi yang mau ditanyain, kak?
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
User: siapa top user saat ini dan group mana paling aktif saat ini
Assistant: Top user saat ini nomor 1 itu 6281937907919 dengan 54 total permintaan. Sedangkan grup yang paling aktif sekarang adalah "A L Ξ X Λ | Whatsapp Bot" dengan 1462 total permintaan.

> Pesan ini menggunakan AI
User: sebutkan semuanya
Assistant: Top user ada si 6281937907919, 6282283728202, dan 6283895454988, semuanya sering banget minta bantuan gua. Untuk grup, yang paling aktif itu "A L Ξ X Λ | Whatsapp Bot" dengan permintaan lebih dari 1462 kali. Seru banget, kan? Ada lagi yang mau lu tahu, kak?
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: sebutkan semuanya
User: alita
Assistant: Hey, kak! Ada yang bisa gua bantuin kali ini? Lagi sibuk apa, nih? 😊
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
Assistant: Hey, kak! Ada yang bisa gua bantuin kali ini? Lagi sibuk apa, nih? 😊

> Pesan ini menggunakan AI
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: alita
User: sibuk mikirin kamu
Assistant: Wah, anjay! Gua jadi geer nih. Hahaha, emang kamu lagi mikirin apaan soal gua? 😄
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: alita
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: alita
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: alita
User: alita
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: alita
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: alita
User: sibuk mikirin kamu
User: alita
User: alita
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
User: sibuk mikirin kamu
Assistant: min, permainan di bot ni ada lagi gak?
User: ada, alita
Assistant:
ASSISTANT
Wah, anjay! Gua jadi geer nih. Hahaha, emang lagi mikirin apaan soal gua? 😄 Ada yang perlu gua bantu, Kak?

turns-00052.parquet:28605

e7b95b72a98f46fa8d173875
turn 1/1gpt-4o-2024-08-06Russianunknown country206 words
degenerate_repetitionAbsentFinal dense release
USER
System: Ответь на русском
User: Как бесплатно создать Майнкрафт сервер
Assistant:
ASSISTANT
Создание бесплатного сервера Minecraft может быть выполнена с помощью различных инструментов и услуг. Вот несколько шагов, которые помогут вам создать сервер:

1. **Используйте бесплатные хостинговые службы**:
   - **Aternos**: Один из самых популярных бесплатных хостингов для Minecraft-серверов. Просто зарегистрируйтесь на сайте, настройте сервер и запускайте его.
   - **Minehut**: Позволяет создать до двух бесплатных серверов с ограниченным количеством слотов.

2. **Локальный хостинг**:
   - Скачайте серверное программное обеспечение с официального сайта Minecraft.
   - Запустите `.jar` файл, чтобы создать необходимые файлы серверной конфигурации.
   - В файле `eula.txt` измените `eula=false` на `eula=true` для принятия условий использования.
   - Настройте файл `server.properties` по вашему усмотрению.
   - Откройте порт 25565 на маршрутизаторе для доступа другим игрокам (так называемый порт-форвардинг).

3. **Использование программ как Hamachi**:
   - Установите Hamachi и создайте новую сеть.
   - Поделитесь идентификатором сети и паролем с друзьями, чтобы они могли подключиться.
   - Запустите локальный сервер и используйте ваш IP-адрес Hamachi для подключения.

4. **Использование Open Source решений**:
   - Можете воспользоваться решениями с открытым исходным кодом, такими как Pterodactyl, для расширенного управления серверами.

Помните, что бесплатные решения могут иметь ограничения на количество игроков, производительность или длительность работы сервера. Если вы планируете крупный проект или серьезные игровые сессии, возможно, стоит рассмотреть платные услуги.

turns-00052.parquet:28606

4cc3a94ae1931a4df314b047
turn 1/1gpt-4o-2024-08-06Englishunknown country372 words
degenerate_repetitionAbsentFinal dense release
USER
Task Name: "Create Generate Random ID Function" 
			Description: "Generate a Node.js function to create a random ID with alphabet characters of a specified length."
			Requirements:
			1. Database: undefined
			2. Output needed: Return only the function code as specified in dependencies
	       
			3. Tasks to implement: Create a function to generate random id in alphabets as long as user input., 
Export the function itself as a module.
	
			4. Supporting data 
			
		Please generate the code for above tasks, following the instructions and including comments for clarity and logic for Dependencies must needed.
	
		While creating router for specifict file your have to read supporing file data and create for that 
	
		 **Output Formats**: 

Return dependencies like this `const bcrypt = require('bcrypt');`  in this formate
```dependencies
const bcrypt = require('bcrypt');
```

Ensure this format is consistently followed in responses.
ASSISTANT
To implement a Node.js function that generates a random ID consisting of alphabet characters of a specified length, we'll need to create a function that satisfies the provided requirements. We'll also make sure to export this function as a module.

Here's the code following the specified instructions:

```js
// Function to generate a random ID with alphabet characters of a specified length
function generateRandomId(length) {
    const alphabets = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    let randomId = '';
    for (let i = 0; i < length; i++) {
        // Generate a random index based on the alphabet length
        const randomIndex = Math.floor(Math.random() * alphabets.length);
        // Add the character at the random index to the randomId
        randomId += alphabets[randomIndex];
    }
    return randomId;
}

// Export the function as a module
module.exports = generateRandomId;
```

### Explanation:
- The function `generateRandomId` takes `length` as an argument, which specifies the length of the random ID to be generated.
- `alphabets` is a string containing all uppercase and lowercase alphabetic characters.
- A `for` loop runs for the specified `length`, selecting a random character from `alphabets` during each iteration and appending it to the `randomId`.
- Finally, the generated `randomId` is returned.
- The function is exported using `module.exports`, making it available for import in other files.

This code does not require any external dependencies like `bcrypt`, so there is no need to include any dependency declarations.

```dependencies
// No additional dependencies are required for this function
```

turns-00052.parquet:28607

f1927e2a0ce1e606ead27ac0
turn 1/1o1-mini-2024-09-12EnglishPakistan6118 words
degenerate_repetitionAbsentFinal dense release
USER
edit this colab code to run on kaggle, import os
import re
import toml
import pathlib
from time import time
from huggingface_hub import HfFileSystem
from IPython.display import Markdown, display

# These carry information from past executions
if "model_url" in globals():
  old_model_url = model_url
else:
  old_model_url = None
if "dependencies_installed" not in globals():
  dependencies_installed = False
if "model_file" not in globals():
  model_file = None

# These may be set by other cells, some are legacy
if "custom_dataset" not in globals():
  custom_dataset = None
if "override_dataset_config_file" not in globals():
  override_dataset_config_file = None
if "continue_from_lora" not in globals():
  continue_from_lora = ""
if "override_config_file" not in globals():
  override_config_file = None

COLAB = True
SOURCE = "https://github.com/qaneel/kohya-trainer"
COMMIT = None
BETTER_EPOCH_NAMES = True
LOAD_TRUNCATED_IMAGES = True
try:
  LOWRAM = int(next(line.split()[1] for line in open('/proc/meminfo') if "MemTotal" in line)) / (1024**2) < 15
except:
  LOWRAM = False

#@title ## 🚩 Start Here

#@markdown ### ▶️ Setup
#@markdown Your project name will be the same as the folder containing your images. Spaces aren't allowed.
project_name = "S1s" #@param {type:"string"}
project_name = project_name.strip()
#@markdown The folder structure doesn't matter and is purely for comfort. Make sure to always pick the same one. I like organizing by project.
folder_structure = "Organize by project (MyDrive/Loras/project_name/dataset)" #@param ["Organize by category (MyDrive/lora_training/datasets/project_name)", "Organize by project (MyDrive/Loras/project_name/dataset)"]
#@markdown Decide the model that will be downloaded and used for training. You can also choose your own by pasting its download link, or a file in your Google Drive starting with `/content/drive/MyDrive`.
training_model = "Stable Diffusion XL 1.0 base" #@param ["Pony Diffusion V6 XL", "Illustrious XL 0.1", "Animagine XL V3", "Stable Diffusion XL 1.0 base"]
optional_custom_training_model = "" #@param {type:"string"}
#@markdown Using a diffusers model utilizes less resources. The 3 options above will work with or without diffusers.
load_diffusers = True #@param {type:"boolean"}
#@markdown Use wandb if you want to visualize the progress of your training over time.
wandb_key = "" #@param {type:"string"}

if optional_custom_training_model:
  model_url = optional_custom_training_model
elif "Pony" in training_model:
  if load_diffusers:
    model_url = "https://huggingface.co/hollowstrawberry/67AB2F"
  else:
    model_url = "https://civitai.com/api/download/models/290640"
  model_file = "/content/ponyDiffusionV6XL.safetensors"
elif "Animagine" in training_model:
  if load_diffusers:
    model_url = "https://huggingface.co/cagliostrolab/animagine-xl-3.0"
  else:
    model_url = "https://civitai.com/api/download/models/293564"
  model_file = "/content/animagineXLV3.safetensors"
elif "Illustrious" in training_model:
  if load_diffusers:
    model_url = "https://huggingface.co/OnomaAIResearch/Illustrious-xl-early-release-v0"
  else:
    model_url = "https://huggingface.co/OnomaAIResearch/Illustrious-xl-early-release-v0/resolve/main/Illustrious-XL-v0.1.safetensors"
else:
  if load_diffusers:
    model_url = "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/"
  else:
    model_url = "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors"

if load_diffusers:
  vae_file= "stabilityai/sdxl-vae"
else:
  vae_url = "https://huggingface.co/stabilityai/sdxl-vae/resolve/main/sdxl_vae.safetensors"
  vae_file = "/content/sdxl_vae.safetensors"

model_url = model_url.strip()

#@markdown ### ▶️ Processing
resolution = 1024 #param {type:"slider", min:768, max:1536, step:128}
caption_extension = ".txt" #@param [".txt", ".caption"]
#@markdown Shuffling anime tags in place improves learning and prompting. An activation tag goes at the start of every text file and will not be shuffled.<p>
shuffle_tags = True #@param {type:"boolean"}
shuffle_caption = shuffle_tags
activation_tags = "1" #@param [0,1,2,3]
keep_tokens = int(activation_tags)

#@markdown ### ▶️ Steps <p>
#@markdown Your images will repeat this number of times during training. I recommend that your images multiplied by their repeats is between 200 and 400.
num_repeats = 10 #@param {type:"number"}
#@markdown Choose how long you want to train for. A good starting point is around 10 epochs or around 2000 steps.<p>
#@markdown One epoch is a number of steps equal to: your number of images multiplied by their repeats, divided by batch size. <p>
preferred_unit = "Steps" #@param ["Epochs", "Steps"]
how_many = 5200 #@param {type:"number"}
max_train_epochs = how_many if preferred_unit == "Epochs" else None
max_train_steps = how_many if preferred_unit == "Steps" else None
#@markdown Saving more epochs will let you compare your Lora's progress better.
save_every_n_epochs = 20 #@param {type:"number"}
keep_only_last_n_epochs = 5 #@param {type:"number"}
if not save_every_n_epochs:
  save_every_n_epochs = max_train_epochs
if not keep_only_last_n_epochs:
  keep_only_last_n_epochs = max_train_epochs

#@markdown ### ▶️ Learning
#@markdown The learning rate is the most important for your results. If your Lora produces black images, lower the unet and text encoder to 1e-4 and 1e-5 respectively, or even lower. <p>
#@markdown If you're training a style you can choose to set the text encoder to 0.
unet_lr = 3e-4 #@param {type:"number"}
text_encoder_lr = 6e-5 #@param {type:"number"}
#@markdown The scheduler is the algorithm that guides the learning rate. If you're not sure, pick `constant` and ignore the number. I personally recommend `cosine_with_restarts` with 3 restarts.
lr_scheduler = "cosine_with_restarts" #@param ["constant", "cosine", "cosine_with_restarts", "constant_with_warmup", "linear", "polynomial"]
lr_scheduler_number = 3 #@param {type:"number"}
#@markdown Steps spent "warming up" the learning rate during training for efficiency. I recommend leaving it at 5%.
lr_warmup_ratio = 0.05 #@param {type:"slider", min:0.0, max:0.2, step:0.01}
lr_warmup_steps = 0
#@markdown Adjusts loss over time, makes learning much more efficient. The paper recommends 5.0, I recommend 8.0 for anime. Higher value makes it less strict. Set to 0 to disable.
min_snr_gamma = 8.0 #@param {type:"slider", min:0.0, max:16.0, step:0.5}
#@markdown Multinoise may help with color balance (darker darks, lighter lights).
multinoise = True #@param {type:"boolean"}

#@markdown ### ▶️ Structure
#@markdown LoRA is the classic type and good for a variety of purposes. LoCon is good with artstyles as it has more layers to learn more aspects of the dataset.
lora_type = "LoRA" #@param ["LoRA", "LoCon"]

#@markdown Below are some recommended XL values for the following settings:

#@markdown | type | network_dim | network_alpha | conv_dim | conv_alpha |
#@markdown | :---: | :---: | :---: | :---: | :---: |
#@markdown | Regular LoRA | 8 | 4 |   |   |
#@markdown | Style LoCon | 16 | 8 | 16 | 8 |

#@markdown More dim means larger Lora, it can hold more information but more isn't always better.
network_dim = 8 #@param {type:"slider", min:1, max:32, step:1}
network_alpha = 4 #@param {type:"slider", min:1, max:32, step:1}
#@markdown The following two values only apply to the additional layers of LoCon.
conv_dim = 4 #@param {type:"slider", min:1, max:32, step:1}
conv_alpha = 1 #@param {type:"slider", min:1, max:32, step:1}

network_module = "networks.lora"
network_args = None
if lora_type.lower() == "locon":
  network_args = [f"conv_dim={conv_dim}", f"conv_alpha={conv_alpha}"]

#@markdown ### ▶️ Training
#@markdown Adjust these parameters depending on your colab configuration.
#@markdown If you're using the free tier you should select a diffusers model at the top of this cell.
#@markdown
#@markdown Higher batch size is often faster but uses more memory.
train_batch_size = 4 #@param {type:"slider", min:1, max:16, step:1}
#@markdown I have found no substantial difference between sdpa and xformers.
cross_attention = "sdpa" #@param ["sdpa", "xformers"]
#@markdown If you're on an A100 you should enable bf16.
mixed_precision = "fp16" #@param ["bf16", "fp16"]
#@markdown Caching latents to drive will add a 250KB file next to each image but will use considerably less memory.
cache_latents = True #@param {type:"boolean"}
cache_latents_to_drive = True #@param {type:"boolean"}
#@markdown The following option will turn off shuffle_tags and disable text encoder training.
cache_text_encoder_outputs  = False  # @param {type:"boolean"}

#@markdown ### ▶️ Advanced
#@markdown The optimizer is the algorithm used for training. AdanW8Bit is the default and works great, while Prodigy manages learning rate automatically and may have several advantages such as training faster due to needing less steps as well as working better for small datasets.
optimizer = "AdamW8bit" #@param ["AdamW8bit", "Prodigy", "DAdaptation", "DadaptAdam", "DadaptLion", "AdamW", "Lion", "SGDNesterov", "SGDNesterov8bit", "AdaFactor"]
#@markdown Recommended args for AdamW8bit: `weight_decay=0.1 betas=[0.9,0.99]`<p>
#@markdown Recommended args for Prodigy: `decouple=True weight_decay=0.01 betas=[0.9,0.999] d_coef=2 use_bias_correction=True safeguard_warmup=True`<p>
#markdown Recommended args for CAME: `weight_decay=0.04`
optimizer_args = "weight_decay=0.1 betas=[0.9,0.99]" #@param {type:"string"}
optimizer_args = [a.strip() for a in optimizer_args.split(' ') if a]
#@markdown If Dadapt or Prodigy are selected and the recommended box is checked, the following recommended values will override any previous settings:<p>
#@markdown `unet_lr=0.75`, `text_encoder_lr=0.75`, `network_alpha=network_dim`<p>
#markdown If CAME + REX is selected and the recommended box is checked, the following recommended values will override any previous settings:<p>
#markdown `unet_lr=1e-4`, `text_encoder_lr=1e-6, betas=default_came_betas`
recommended_values = True #@param {type:"boolean"}

if any(opt in optimizer.lower() for opt in ["dadapt", "prodigy"]):
  if recommended_values:
    unet_lr = 0.75
    text_encoder_lr = 0.75
    network_alpha = network_dim
elif "CAME" in optimizer:
  optimizer = "CAME"
  lr_scheduler = "REX"
  if recommended_values:
    unet_lr = 1e-4
    text_encoder_lr = 1e-6
    for i in range(len(optimizer_args)):
      if "betas" in optimizer_args[i]:
        optimizer_args.pop(i)
        break

lr_scheduler_num_cycles = lr_scheduler_number if lr_scheduler == "cosine_with_restarts" else 0
lr_scheduler_power = lr_scheduler_number if lr_scheduler == "polynomial" else 0


#@markdown ### ▶️ Ready
#@markdown You can now run this cell to cook your Lora. Good luck! <p>


# 👩‍💻 Cool code goes here

root_dir = "/content" if COLAB else pathlib.Path.home() / "Loras"
deps_dir = os.path.join(root_dir, "deps")
repo_dir = os.path.join(root_dir, "kohya-trainer")

if "/Loras" in folder_structure:
  main_dir      = os.path.join(root_dir, "drive/MyDrive/Loras") if COLAB else root_dir
  log_folder    = os.path.join(main_dir, "_logs")
  config_folder = os.path.join(main_dir, project_name)
  images_folder = os.path.join(main_dir, project_name, "dataset")
  output_folder = os.path.join(main_dir, project_name, "output")
else:
  main_dir      = os.path.join(root_dir, "drive/MyDrive/lora_training") if COLAB else root_dir
  images_folder = os.path.join(main_dir, "datasets", project_name)
  output_folder = os.path.join(main_dir, "output", project_name)
  config_folder = os.path.join(main_dir, "config", project_name)
  log_folder    = os.path.join(main_dir, "log")

config_file = os.path.join(config_folder, "training_config.toml")
dataset_config_file = os.path.join(config_folder, "dataset_config.toml")
accelerate_config_file = os.path.join(repo_dir, "accelerate_config/config.yaml")

def install_dependencies():
  os.chdir(root_dir)
  !git clone {SOURCE} {repo_dir}
  os.chdir(repo_dir)
  if COMMIT:
    !git reset --hard {COMMIT}
  !wget https://raw.githubusercontent.com/hollowstrawberry/kohya-colab/main/train_network_xl_wrapper.py -q -O train_network_xl_wrapper.py
  !wget https://raw.githubusercontent.com/hollowstrawberry/kohya-colab/main/dracula.py -q -O dracula.py

  !apt -y update -qq
  !apt -y install aria2 -qq
  !pip install torch==2.5.1+cu121 accelerate==0.19.0 transformers==4.30.2 diffusers==0.18.2 \
    bitsandbytes==0.40.0.post4 opencv-python jax==0.4.23 jaxlib==0.4.23 \
    pytorch-lightning==1.9.0 voluptuous==0.13.1 toml==0.10.2 ftfy==6.1.1 einops==0.6.0 \
    safetensors pygments huggingface-hub==0.22.0 wandb invisible-watermark==0.2.0 open-clip-torch==2.20.0 \
    dadaptation==3.1 prodigyopt==1.0 lion-pytorch==0.1.2
  !pip install -e .
  if cross_attention == "xformers":
    !pip install -q xformers==0.0.26.dev778
  if "CAME" in optimizer:
    !pip install came-pytorch
    !wget https://raw.githubusercontent.com/hollowstrawberry/kohya-colab/main/train_util.py -q -O library/train_util.py

  # patch kohya for minor stuff
  if LOWRAM:
    !sed -i "s@cpu@cuda@" library/model_util.py
  if LOAD_TRUNCATED_IMAGES:
    !sed -i 's/from PIL import Image/from PIL import Image, ImageFile\nImageFile.LOAD_TRUNCATED_IMAGES=True/g' library/train_util.py # fix truncated jpegs error
  if BETTER_EPOCH_NAMES:
    !sed -i 's/{:06d}/{:02d}/g' library/train_util.py # make epoch names shorter
    !sed -i 's/"." + args.save_model_as)/"-{:02d}.".format(num_train_epochs) + args.save_model_as)/g' train_network.py # name of the last epoch will match the rest

  from accelerate.utils import write_basic_config
  if not os.path.exists(accelerate_config_file):
    write_basic_config(save_location=accelerate_config_file)

  os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
  os.environ["BITSANDBYTES_NOWELCOME"] = "1"
  os.environ["SAFETENSORS_FAST_GPU"] = "1"

def validate_dataset():
  global lr_warmup_steps, lr_warmup_ratio, caption_extension, keep_tokens, model_url
  supported_types = (".png", ".jpg", ".jpeg", ".webp", ".bmp")

  if model_url.startswith("/content/drive/") and not os.path.exists(model_url):
    print("💥 Error: The custom training model you specified was not found in your Google Drive.")
    return

  print("\n💿 Checking dataset...")
  if not project_name.strip() or any(c in project_name for c in " .()\"'\\/"):
    print("💥 Error: Please choose a valid project name.")
    return

  # Find the folders and files
  if custom_dataset:
    try:
      datconf = toml.loads(custom_dataset)
      datasets = [d for d in datconf["datasets"][0]["subsets"]]
    except:
      print(f"💥 Error: Your custom dataset is invalid or contains an error! Please check the original template.")
      return
    reg = [d.get("image_dir") for d in datasets if d.get("is_reg", False)]
    datasets_dict = {d["image_dir"]: d["num_repeats"] for d in datasets}
    folders = datasets_dict.keys()
    files = [f for folder in folders for f in os.listdir(folder)]
    images_repeats = {folder: (len([f for f in os.listdir(folder) if f.lower().endswith(supported_types)]), datasets_dict[folder]) for folder in folders}
  else:
    reg = []
    folders = [images_folder]
    files = os.listdir(images_folder)
    images_repeats = {images_folder: (len([f for f in files if f.lower().endswith(supported_types)]), num_repeats)}

  # Validation
  for folder in folders:
    if not os.path.exists(folder):
      print(f"💥 Error: The folder {folder.replace('/content/drive/', '')} doesn't exist.")
      return
  for folder, (img, rep) in images_repeats.items():
    if not img:
      print(f"💥 Error: Your {folder.replace('/content/drive/', '')} folder is empty.")
      return
  test_files = []
  for f in files:
    if not f.lower().endswith((caption_extension, ".npz")) and not f.lower().endswith(supported_types):
      print(f"💥 Error: Invalid file in dataset: \"{f}\". Aborting.")
      return
    for ff in test_files:
      if f.endswith(supported_types) and ff.endswith(supported_types) \
          and os.path.splitext(f)[0] == os.path.splitext(ff)[0]:
        print(f"💥 Error: The files {f} and {ff} cannot have the same name. Aborting.")
        return
    test_files.append(f)

  if caption_extension and not [txt for txt in files if txt.lower().endswith(caption_extension)]:
    caption_extension = ""
  if continue_from_lora and not (continue_from_lora.endswith(".safetensors") and os.path.exists(continue_from_lora)):
    print(f"💥 Error: Invalid path to existing Lora. Example: /content/drive/MyDrive/Loras/example.safetensors")
    return

  # Pretty stuff

  pre_steps_per_epoch = sum(img*rep for (img, rep) in images_repeats.values())
  steps_per_epoch = pre_steps_per_epoch/train_batch_size
  total_steps = max_train_steps or int(max_train_epochs*steps_per_epoch)
  estimated_epochs = int(total_steps/steps_per_epoch)
  lr_warmup_steps = int(total_steps*lr_warmup_ratio)

  for folder, (img, rep) in images_repeats.items():
    print("📁"+folder.replace("/content/drive/", "") + (" (Regularization)" if folder in reg else ""))
    print(f"📈 Found {img} images with {rep} repeats, equaling {img*rep} steps.")
  print(f"📉 Divide {pre_steps_per_epoch} steps by {train_batch_size} batch size to get {steps_per_epoch} steps per epoch.")
  if max_train_epochs:
    print(f"🔮 There will be {max_train_epochs} epochs, for around {total_steps} total training steps.")
  else:
    print(f"🔮 There will be {total_steps} steps, divided into {estimated_epochs} epochs and then some.")

  if total_steps > 10000:
    print("💥 Error: Your total steps are too high. You probably made a mistake. Aborting...")
    return

  return True

def create_config():
  global dataset_config_file, config_file, model_file

  if override_config_file:
    config_file = override_config_file
    print(f"\n⭕ Using custom config file {config_file}")
  else:
    config_dict = {
      "network_arguments": {
        "unet_lr": unet_lr,
        "text_encoder_lr": text_encoder_lr if not cache_text_encoder_outputs else 0,
        "network_dim": network_dim,
        "network_alpha": network_alpha,
        "network_module": network_module,
        "network_args": network_args,
        "network_train_unet_only": text_encoder_lr == 0 or cache_text_encoder_outputs,
        "network_weights": continue_from_lora if continue_from_lora else None
      },
      "optimizer_arguments": {
        "learning_rate": unet_lr,
        "lr_scheduler": lr_scheduler,
        "lr_scheduler_num_cycles": lr_scheduler_num_cycles if lr_scheduler == "cosine_with_restarts" else None,
        "lr_scheduler_power": lr_scheduler_power if lr_scheduler == "polynomial" else None,
        "lr_warmup_steps": lr_warmup_steps if lr_scheduler != "constant" else None,
        "optimizer_type": optimizer,
        "optimizer_args": optimizer_args if optimizer_args else None,
      },
      "training_arguments": {
        "pretrained_model_name_or_path": model_file,
        "vae": vae_file,
        "max_train_steps": max_train_steps,
        "max_train_epochs": max_train_epochs,
        "train_batch_size": train_batch_size,
        "seed": 42,
        "max_token_length": 225,
        "xformers": cross_attention == "xformers",
        "sdpa": cross_attention == "sdpa",
        "min_snr_gamma": min_snr_gamma if min_snr_gamma > 0 else None,
        "lowram": LOWRAM,
        "no_half_vae": True,
        "gradient_checkpointing": True,
        "gradient_accumulation_steps": 1,
        "max_data_loader_n_workers": 8,
        "persistent_data_loader_workers": True,
        "mixed_precision": mixed_precision,
        "full_bf16": mixed_precision == "bf16",
        "cache_latents": cache_latents,
        "cache_latents_to_disk": cache_latents_to_drive,
        "cache_text_encoder_outputs": cache_text_encoder_outputs,
        "min_timestep": 0,
        "max_timestep": 1000,
        "prior_loss_weight": 1.0,
        "multires_noise_iterations": 6 if multinoise else None,
        "multires_noise_discount": 0.3 if multinoise else None,
      },
      "saving_arguments": {
        "save_precision": "fp16",
        "save_model_as": "safetensors",
        "save_every_n_epochs": save_every_n_epochs,
        "save_last_n_epochs": keep_only_last_n_epochs,
        "output_name": project_name,
        "output_dir": output_folder,
        "log_prefix": project_name,
        "logging_dir": log_folder,
        "wandb_api_key": wandb_key if wandb_key else None,
        "log_with": "wandb" if wandb_key else None,
      }
    }

    for key in config_dict:
      if isinstance(config_dict[key], dict):
        config_dict[key] = {k: v for k, v in config_dict[key].items() if v is not None}

    with open(config_file, "w") as f:
      f.write(toml.dumps(config_dict))
    print(f"\n📄 Config saved to {config_file}")

  if override_dataset_config_file:
    dataset_config_file = override_dataset_config_file
    print(f"⭕ Using custom dataset config file {dataset_config_file}")
  else:
    dataset_config_dict = {
      "general": {
        "resolution": resolution,
        "shuffle_caption": shuffle_caption and not cache_text_encoder_outputs,
        "keep_tokens": keep_tokens,
        "flip_aug": False,
        "caption_extension": caption_extension,
        "enable_bucket": True,
        "bucket_no_upscale": False,
        "bucket_reso_steps": 64,
        "min_bucket_reso": 256,
        "max_bucket_reso": 4096,
      },
      "datasets": toml.loads(custom_dataset)["datasets"] if custom_dataset else [
        {
          "subsets": [
            {
              "num_repeats": num_repeats,
              "image_dir": images_folder,
              "class_tokens": None if caption_extension else project_name
            }
          ]
        }
      ]
    }

    for key in dataset_config_dict:
      if isinstance(dataset_config_dict[key], dict):
        dataset_config_dict[key] = {k: v for k, v in dataset_config_dict[key].items() if v is not None}

    with open(dataset_config_file, "w") as f:
      f.write(toml.dumps(dataset_config_dict))
    print(f"📄 Dataset config saved to {dataset_config_file}")

def download_model():
  global old_model_url, model_url, model_file, vae_url, vae_file
  real_model_url = model_url  # There was a reason for having a separate variable but I forgot what it was.

  if real_model_url.startswith("/content/drive/"):
    # Local model, already checked to exist
    model_file = real_model_url
    print(f"📁 Using local model file: {model_file}")
    # Validation
    if model_file.lower().endswith(".safetensors"):
      from safetensors.torch import load_file as load_safetensors
      try:
        test = load_safetensors(model_file)
        del test
      except:
        return False
    elif model_file.lower().endswith(".ckpt"):
      from torch import load as load_ckpt
      try:
        test = load_ckpt(model_file)
        del test
      except:
        return False
    return True

  else:
    # Downloadable model
    if load_diffusers:
      if 'huggingface.co' in real_model_url:
          match = re.search(r'huggingface\.co/([^/]+)/([^/]+)', real_model_url)
          if match:
              username = match.group(1)
              model_name = match.group(2)
              model_file = f"{username}/{model_name}"
              fs = HfFileSystem()
              existing_folders = set(fs.ls(model_file, detail=False))
              necessary_folders = [ "scheduler", "text_encoder", "text_encoder_2", "tokenizer", "tokenizer_2", "unet", "vae" ]
              if all(f"{model_file}/{folder}" in existing_folders for folder in necessary_folders):
                print("🍃 Diffusers model identified.")  # Will be handled by kohya
                return True
      raise ValueError("💥 Failed to load Diffusers model. If this model is not Diffusers, have you tried turning it off at the top of the colab?")

    # Define local filename
    if not model_file:
      if real_model_url.lower().endswith((".ckpt", ".safetensors")):
        model_file = f"/content{real_model_url[real_model_url.rfind('/'):]}"
      else:
        model_file = "/content/downloaded_model.safetensors"
        if os.path.exists(model_file):
          !rm "{model_file}"

    # HuggingFace
    if m := re.search(r"(?:https?://)?(?:www\.)?huggingface\.co/[^/]+/[^/]+/blob", real_model_url):
      real_model_url = real_model_url.replace("blob", "resolve")
    # Civitai
    elif m := re.search(r"(?:https?://)?(?:www\\.)?civitai\.com/models/([0-9]+)(/[A-Za-z0-9-_]+)?", real_model_url):
      if m.group(2):
        model_file = f"/content{m.group(2)}.safetensors"
      if m := re.search(r"modelVersionId=([0-9]+)", real_model_url):
        real_model_url = f"https://civitai.com/api/download/models/{m.group(1)}"
      else:
        raise ValueError("💥 optional_custom_training_model contains a civitai link, but the link doesn't include a modelVersionId. You can also right click the download button to copy the direct download link.")

    # Download checkpoint
    !aria2c "{real_model_url}" --console-log-level=warn -c -s 16 -x 16 -k 10M -d / -o "{model_file}"

    # Download VAE
    if not os.path.exists(vae_file):
      !aria2c "{vae_url}" --console-log-level=warn -c -s 16 -x 16 -k 10M -d / -o "{vae_file}"

    # Validation

    if model_file.lower().endswith(".safetensors"):
      from safetensors.torch import load_file as load_safetensors
      try:
        test = load_safetensors(model_file)
        del test
      except:
        new_model_file = os.path.splitext(model_file)[0]+".ckpt"
        !mv "{model_file}" "{new_model_file}"
        model_file = new_model_file
        print(f"Renamed model to {os.path.splitext(model_file)[0]}.ckpt")

    if model_file.lower().endswith(".ckpt"):
      from torch import load as load_ckpt
      try:
        test = load_ckpt(model_file)
        del test
      except:
        return False

  return True

def main():
  global dependencies_installed

  if COLAB and not os.path.exists('/content/drive'):
    from google.colab import drive
    print("📂 Connecting to Google Drive...")
    drive.mount('/content/drive')

  for dir in (main_dir, deps_dir, repo_dir, log_folder, images_folder, output_folder, config_folder):
    os.makedirs(dir, exist_ok=True)

  if not validate_dataset():
    return

  if not dependencies_installed:
    print("\n🏭 Installing dependencies...\n")
    t0 = time()
    install_dependencies()
    t1 = time()
    dependencies_installed = True
    print(f"\n✅ Installation finished in {int(t1-t0)} seconds.")
  else:
    print("\n✅ Dependencies already installed.")

  if old_model_url != model_url or not model_file or not os.path.exists(model_file):
    print("\n🔄 Getting model...")
    if not download_model():
      print("\n💥 Error: The model you specified is invalid or corrupted."
            "\nIf you're using an URL, please check that the model is accessible without being logged in."
            "\nYou can try civitai or huggingface URLs, or a path in your Google Drive starting with /content/drive/MyDrive")
      return
    print()
  else:
    print("\n🔄 Model already downloaded.\n")

  create_config()

  print("\n⭐ Starting trainer...\n")
  os.chdir(repo_dir)

  !accelerate launch --quiet --config_file={accelerate_config_file} --num_cpu_threads_per_process=1 train_network_xl_wrapper.py --dataset_config={dataset_config_file} --config_file={config_file}

  if not get_ipython().__dict__['user_ns']['_exit_code']:
    display(Markdown("### ✅ Done! [Go download your Lora from Google Drive](https://drive.google.com/drive/my-drive)\n"
                     "### There will be several files, you should try the latest version (the file with the largest number next to it)"))

main()
ASSISTANT
To run the provided Colab code on Kaggle, several adjustments are necessary. Kaggle's environment differs from Colab in terms of directory structure, dependency management, and data handling. Below are the key changes along with the modified code:

### **Key Changes:**

1. **Remove Colab-Specific Code:**
   - **Google Drive Mounting:** Kaggle doesn't support mounting Google Drive. Instead, you can use Kaggle Datasets or upload files directly.
   - **Path Adjustments:** Replace `/content` (Colab) with Kaggle's directory structure (`/kaggle/working` for output and `/kaggle/input` for input datasets).

2. **Adjust Directory Structures:**
   - **Root Directory:** Set `root_dir` appropriately for Kaggle.
   - **Data Paths:** Ensure all data paths point to `/kaggle/input` or `/kaggle/working`.

3. **Dependency Management:**
   - **Installation:** Use `!pip install` for any additional dependencies not pre-installed on Kaggle. Note that Kaggle restricts certain installations, so ensure compatibility.
   - **System Packages:** Avoid using `apt-get` as Kaggle has limited support. If necessary, leverage available packages or use alternative methods.

4. **Environment Variables:**
   - **Omit Colab-Specific Variables:** Remove or adjust environment variables that are specific to Colab.

5. **Miscellaneous Adjustments:**
   - **Shell Commands:** Adjust any file paths or commands to align with Kaggle's environment.
   - **Output Handling:** Ensure outputs are saved to `/kaggle/working` or other appropriate directories.

### **Modified Code for Kaggle:**

```python
import os
import re
import toml
import pathlib
from time import time
from huggingface_hub import HfFileSystem
from IPython.display import Markdown, display

# Set COLAB to False for Kaggle
COLAB = False

# These carry information from past executions
if "model_url" in globals():
    old_model_url = model_url
else:
    old_model_url = None
if "dependencies_installed" not in globals():
    dependencies_installed = False
if "model_file" not in globals():
    model_file = None

# These may be set by other cells, some are legacy
if "custom_dataset" not in globals():
    custom_dataset = None
if "override_dataset_config_file" not in globals():
    override_dataset_config_file = None
if "continue_from_lora" not in globals():
    continue_from_lora = ""
if "override_config_file" not in globals():
    override_config_file = None

SOURCE = "https://github.com/qaneel/kohya-trainer"
COMMIT = None
BETTER_EPOCH_NAMES = True
LOAD_TRUNCATED_IMAGES = True
try:
    LOWRAM = False  # Kaggle offers more memory; adjust if necessary
except:
    LOWRAM = False

#@title ## 🚩 Start Here

#@markdown ### ▶️ Setup
#@markdown Your project name will be the same as the folder containing your images. Spaces aren't allowed.
project_name = "S1s"  #@param {type:"string"}
project_name = project_name.strip()
#@markdown The folder structure doesn't matter and is purely for comfort. Make sure to always pick the same one. I like organizing by project.
folder_structure = "Organize by project (/kaggle/working/Loras/project_name/dataset)"  #@param ["Organize by category (/kaggle/working/lora_training/datasets/project_name)", "Organize by project (/kaggle/working/Loras/project_name/dataset)"]
#@markdown Decide the model that will be downloaded and used for training. You can also choose your own by pasting its download link, or a file in your Kaggle input starting with `/kaggle/input`.
training_model = "Stable Diffusion XL 1.0 base"  #@param ["Pony Diffusion V6 XL", "Illustrious XL 0.1", "Animagine XL V3", "Stable Diffusion XL 1.0 base"]
optional_custom_training_model = ""  #@param {type:"string"}
#@markdown Using a diffusers model utilizes less resources. The 3 options above will work with or without diffusers.
load_diffusers = True  #@param {type:"boolean"}
#@markdown Use wandb if you want to visualize the progress of your training over time.
wandb_key = ""  #@param {type:"string"}

if optional_custom_training_model:
    model_url = optional_custom_training_model
elif "Pony" in training_model:
    if load_diffusers:
        model_url = "https://huggingface.co/hollowstrawberry/67AB2F"
    else:
        model_url = "https://civitai.com/api/download/models/290640"
    model_file = "/kaggle/working/ponyDiffusionV6XL.safetensors"
elif "Animagine" in training_model:
    if load_diffusers:
        model_url = "https://huggingface.co/cagliostrolab/animagine-xl-3.0"
    else:
        model_url = "https://civitai.com/api/download/models/293564"
    model_file = "/kaggle/working/animagineXLV3.safetensors"
elif "Illustrious" in training_model:
    if load_diffusers:
        model_url = "https://huggingface.co/OnomaAIResearch/Illustrious-xl-early-release-v0"
    else:
        model_url = "https://huggingface.co/OnomaAIResearch/Illustrious-xl-early-release-v0/resolve/main/Illustrious-XL-v0.1.safetensors"
else:
    if load_diffusers:
        model_url = "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/"
    else:
        model_url = "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors"

if load_diffusers:
    vae_file = "stabilityai/sdxl-vae"
else:
    vae_url = "https://huggingface.co/stabilityai/sdxl-vae/resolve/main/sdxl_vae.safetensors"
    vae_file = "/kaggle/working/sdxl_vae.safetensors"

model_url = model_url.strip()

#@markdown ### ▶️ Processing
resolution = 1024  #param {type:"slider", min:768, max:1536, step:128}
caption_extension = ".txt"  #@param [".txt", ".caption"]
#@markdown Shuffling anime tags in place improves learning and prompting. An activation tag goes at the start of every text file and will not be shuffled.<p>
shuffle_tags = True  #@param {type:"boolean"}
shuffle_caption = shuffle_tags
activation_tags = "1"  #@param [0,1,2,3]
keep_tokens = int(activation_tags)

#@markdown ### ▶️ Steps <p>
#@markdown Your images will repeat this number of times during training. I recommend that your images multiplied by their repeats is between 200 and 400.
num_repeats = 10  #@param {type:"number"}
#@markdown Choose how long you want to train for. A good starting point is around 10 epochs or around 2000 steps.<p>
#@markdown One epoch is a number of steps equal to: your number of images multiplied by their repeats, divided by batch size. <p>
preferred_unit = "Steps"  #@param ["Epochs", "Steps"]
how_many = 5200  #@param {type:"number"}
max_train_epochs = how_many if preferred_unit == "Epochs" else None
max_train_steps = how_many if preferred_unit == "Steps" else None
#@markdown Saving more epochs will let you compare your Lora's progress better.
save_every_n_epochs = 20  #@param {type:"number"}
keep_only_last_n_epochs = 5  #@param {type:"number"}
if not save_every_n_epochs:
    save_every_n_epochs = max_train_epochs
if not keep_only_last_n_epochs:
    keep_only_last_n_epochs = max_train_epochs

#@markdown ### ▶️ Learning
#@markdown The learning rate is the most important for your results. If your Lora produces black images, lower the unet and text encoder to 1e-4 and 1e-5 respectively, or even lower. <p>
#@markdown If you're training a style you can choose to set the text encoder to 0.
unet_lr = 3e-4  #@param {type:"number"}
text_encoder_lr = 6e-5  #@param {type:"number"}
#@markdown The scheduler is the algorithm that guides the learning rate. If you're not sure, pick `constant` and ignore the number. I personally recommend `cosine_with_restarts` with 3 restarts.
lr_scheduler = "cosine_with_restarts"  #@param ["constant", "cosine", "cosine_with_restarts", "constant_with_warmup", "linear", "polynomial"]
lr_scheduler_number = 3  #@param {type:"number"}
#@markdown Steps spent "warming up" the learning rate during training for efficiency. I recommend leaving it at 5%.
lr_warmup_ratio = 0.05  #@param {type:"slider", min:0.0, max:0.2, step:0.01}
lr_warmup_steps = 0
#@markdown Adjusts loss over time, makes learning much more efficient. The paper recommends 5.0, I recommend 8.0 for anime. Higher value makes it less strict. Set to 0 to disable.
min_snr_gamma = 8.0  #@param {type:"slider", min:0.0, max:16.0, step:0.5}
#@markdown Multinoise may help with color balance (darker darks, lighter lights).
multinoise = True  #@param {type:"boolean"}

#@markdown ### ▶️ Structure
#@markdown LoRA is the classic type and good for a variety of purposes. LoCon is good with artstyles as it has more layers to learn more aspects of the dataset.
lora_type = "LoRA"  #@param ["LoRA", "LoCon"]

#@markdown Below are some recommended XL values for the following settings:

#@markdown | type | network_dim | network_alpha | conv_dim | conv_alpha |
#@markdown | :---: | :---: | :---: | :---: | :---: |
#@markdown | Regular LoRA | 8 | 4 |   |   |
#@markdown | Style LoCon | 16 | 8 | 16 | 8 |

#@markdown More dim means larger Lora, it can hold more information but more isn't always better.
network_dim = 8  #@param {type:"slider", min:1, max:32, step:1}
network_alpha = 4  #@param {type:"slider", min:1, max:32, step:1}
#@markdown The following two values only apply to the additional layers of LoCon.
conv_dim = 4  #@param {type:"slider", min:1, max:32, step:1}
conv_alpha = 1  #@param {type:"slider", min:1, max:32, step:1}

network_module = "networks.lora"
network_args = None
if lora_type.lower() == "locon":
    network_args = [f"conv_dim={conv_dim}", f"conv_alpha={conv_alpha}"]

#@markdown ### ▶️ Training
#@markdown Adjust these parameters depending on your Kaggle configuration.
#@markdown If you're using a lower tier, you should select a diffusers model at the top of this cell.
#@markdown
#@markdown Higher batch size is often faster but uses more memory.
train_batch_size = 4  #@param {type:"slider", min:1, max:16, step:1}
#@markdown I have found no substantial difference between sdpa and xformers.
cross_attention = "sdpa"  #@param ["sdpa", "xformers"]
#@markdown If you're using a GPU that supports bf16, you can enable it. Otherwise, use fp16.
mixed_precision = "fp16"  #@param ["bf16", "fp16"]
#@markdown Caching latents to drive will add a 250KB file next to each image but will use considerably less memory.
cache_latents = True  #@param {type:"boolean"}
cache_latents_to_drive = False  #@param {type:"boolean"}  # Not applicable in Kaggle
#@markdown The following option will turn off shuffle_tags and disable text encoder training.
cache_text_encoder_outputs = False  # @param {type:"boolean"}

#@markdown ### ▶️ Advanced
#@markdown The optimizer is the algorithm used for training. AdanW8Bit is the default and works great, while Prodigy manages learning rate automatically and may have several advantages such as training faster due to needing less steps as well as working better for small datasets.
optimizer = "AdamW8bit"  #@param ["AdamW8bit", "Prodigy", "DAdaptation", "DadaptAdam", "DadaptLion", "AdamW", "Lion", "SGDNesterov", "SGDNesterov8bit", "AdaFactor"]
#@markdown Recommended args for AdamW8bit: `weight_decay=0.1 betas=[0.9,0.99]`<p>
#@markdown Recommended args for Prodigy: `decouple=True weight_decay=0.01 betas=[0.9,0.999] d_coef=2 use_bias_correction=True safeguard_warmup=True`<p>
#markdown Recommended args for CAME: `weight_decay=0.04`
optimizer_args = "weight_decay=0.1 betas=[0.9,0.99]"  #@param {type:"string"}
optimizer_args = [a.strip() for a in optimizer_args.split(' ') if a]
#@markdown If Dadapt or Prodigy are selected and the recommended box is checked, the following recommended values will override any previous settings:<p>
#@markdown `unet_lr=0.75`, `text_encoder_lr=0.75`, `network_alpha=network_dim`<p>
#markdown If CAME + REX is selected and the recommended box is checked, the following recommended values will override any previous settings:<p>
#markdown `unet_lr=1e-4`, `text_encoder_lr=1e-6, betas=default_came_betas`
recommended_values = True  #@param {type:"boolean"}

if any(opt in optimizer.lower() for opt in ["dadapt", "prodigy"]):
    if recommended_values:
        unet_lr = 0.75
        text_encoder_lr = 0.75
        network_alpha = network_dim
elif "CAME" in optimizer:
    optimizer = "CAME"
    lr_scheduler = "REX"
    if recommended_values:
        unet_lr = 1e-4
        text_encoder_lr = 1e-6
        for i in range(len(optimizer_args)):
            if "betas" in optimizer_args[i]:
                optimizer_args.pop(i)
                break

lr_scheduler_num_cycles = lr_scheduler_number if lr_scheduler == "cosine_with_restarts" else 0
lr_scheduler_power = lr_scheduler_number if lr_scheduler == "polynomial" else 0

#@markdown ### ▶️ Ready
#@markdown You can now run this cell to cook your Lora. Good luck! <p>


# 👩‍💻 Cool code goes here

# Define root directory for Kaggle
root_dir = "/kaggle/working"
deps_dir = os.path.join(root_dir, "deps")
repo_dir = os.path.join(root_dir, "kohya-trainer")

if "/Loras" in folder_structure:
    main_dir = os.path.join(root_dir, "Loras")
    log_folder = os.path.join(main_dir, "_logs")
    config_folder = os.path.join(main_dir, project_name)
    images_folder = os.path.join(main_dir, project_name, "dataset")
    output_folder = os.path.join(main_dir, project_name, "output")
else:
    main_dir = os.path.join(root_dir, "lora_training")
    images_folder = os.path.join(main_dir, "datasets", project_name)
    output_folder = os.path.join(main_dir, "output", project_name)
    config_folder = os.path.join(main_dir, "config", project_name)
    log_folder = os.path.join(main_dir, "log")

config_file = os.path.join(config_folder, "training_config.toml")
dataset_config_file = os.path.join(config_folder, "dataset_config.toml")
accelerate_config_file = os.path.join(repo_dir, "accelerate_config/config.yaml")

def install_dependencies():
    os.chdir(root_dir)
    if not os.path.exists(repo_dir):
        !git clone {SOURCE} {repo_dir}
    os.chdir(repo_dir)
    if COMMIT:
        !git reset --hard {COMMIT}
    !wget https://raw.githubusercontent.com/hollowstrawberry/kohya-colab/main/train_network_xl_wrapper.py -q -O train_network_xl_wrapper.py
    !wget https://raw.githubusercontent.com/hollowstrawberry/kohya-colab/main/dracula.py -q -O dracula.py

    # Kaggle may restrict apt installations; proceed if necessary
    # !apt -y update -qq
    # !apt -y install aria2 -qq

    # Install necessary Python packages
    !pip install --quiet torch==2.5.1+cu121 accelerate==0.19.0 transformers==4.30.2 diffusers==0.18.2 \
        bitsandbytes==0.40.0.post4 opencv-python jax==0.4.23 jaxlib==0.4.23 \
        pytorch-lightning==1.9.0 voluptuous==0.13.1 toml==0.10.2 ftfy==6.1.1 einops==0.6.0 \
        safetensors pygments huggingface-hub==0.22.0 wandb invisible-watermark==0.2.0 open-clip-torch==2.20.0 \
        dadaptation==3.1 prodigyopt==1.0 lion-pytorch==0.1.2
    !pip install -e .
    if cross_attention == "xformers":
        !pip install -q xformers==0.0.26.dev778
    if "CAME" in optimizer:
        !pip install came-pytorch
        !wget https://raw.githubusercontent.com/hollowstrawberry/kohya-colab/main/train_util.py -q -O library/train_util.py

    # Patch kohya for minor stuff
    if LOWRAM:
        !sed -i "s@cpu@cuda@" library/model_util.py
    if LOAD_TRUNCATED_IMAGES:
        !sed -i 's/from PIL import Image/from PIL import Image, ImageFile\nImageFile.LOAD_TRUNCATED_IMAGES=True/g' library/train_util.py  # fix truncated jpegs error
    if BETTER_EPOCH_NAMES:
        !sed -i 's/{:06d}/{:02d}/g' library/train_util.py  # make epoch names shorter
        !sed -i 's/"." + args.save_model_as)/"-{:02d}.".format(num_train_epochs) + args.save_model_as)/g' train_network.py  # name of the last epoch will match the rest

    from accelerate.utils import write_basic_config
    if not os.path.exists(accelerate_config_file):
        write_basic_config(save_location=accelerate_config_file)

    os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
    os.environ["BITSANDBYTES_NOWELCOME"] = "1"
    os.environ["SAFETENSORS_FAST_GPU"] = "1"

def validate_dataset():
    global lr_warmup_steps, lr_warmup_ratio, caption_extension, keep_tokens, model_url
    supported_types = (".png", ".jpg", ".jpeg", ".webp", ".bmp")

    if model_url.startswith("/kaggle/input/") and not os.path.exists(model_url):
        print("💥 Error: The custom training model you specified was not found in your Kaggle input.")
        return

    print("\n💿 Checking dataset...")
    if not project_name.strip() or any(c in project_name for c in " .()\"'\\/"):
        print("💥 Error: Please choose a valid project name.")
        return

    # Find the folders and files
    if custom_dataset:
        try:
            datconf = toml.loads(custom_dataset)
            datasets = [d for d in datconf["datasets"][0]["subsets"]]
        except:
            print(f"💥 Error: Your custom dataset is invalid or contains an error! Please check the original template.")
            return
        reg = [d.get("image_dir") for d in datasets if d.get("is_reg", False)]
        datasets_dict = {d["image_dir"]: d["num_repeats"] for d in datasets}
        folders = datasets_dict.keys()
        files = [f for folder in folders for f in os.listdir(folder)]
        images_repeats = {folder: (len([f for f in os.listdir(folder) if f.lower().endswith(supported_types)]), datasets_dict[folder]) for folder in folders}
    else:
        reg = []
        folders = [images_folder]
        if not os.path.exists(images_folder):
            print(f"💥 Error: Images folder {images_folder} does not exist.")
            return
        files = os.listdir(images_folder)
        images_repeats = {images_folder: (len([f for f in files if f.lower().endswith(supported_types)]), num_repeats)}

    # Validation
    for folder in folders:
        if not os.path.exists(folder):
            print(f"💥 Error: The folder {folder} doesn't exist.")
            return
    for folder, (img, rep) in images_repeats.items():
        if not img:
            print(f"💥 Error: Your {folder} folder is empty.")
            return
    test_files = []
    for f in files:
        if not f.lower().endswith((caption_extension, ".npz")) and not f.lower().endswith(supported_types):
            print(f"💥 Error: Invalid file in dataset: \"{f}\". Aborting.")
            return
        for ff in test_files:
            if f.endswith(supported_types) and ff.endswith(supported_types) \
               and os.path.splitext(f)[0] == os.path.splitext(ff)[0]:
                print(f"💥 Error: The files {f} and {ff} cannot have the same name. Aborting.")
                return
        test_files.append(f)

    if caption_extension and not [txt for txt in files if txt.lower().endswith(caption_extension)]:
        caption_extension = ""
    if continue_from_lora and not (continue_from_lora.endswith(".safetensors") and os.path.exists(continue_from_lora)):
        print(f"💥 Error: Invalid path to existing Lora. Example: /kaggle/working/Loras/example.safetensors")
        return

    # Pretty stuff

    pre_steps_per_epoch = sum(img * rep for (img, rep) in images_repeats.values())
    steps_per_epoch = pre_steps_per_epoch / train_batch_size
    total_steps = max_train_steps or int(max_train_epochs * steps_per_epoch)
    estimated_epochs = int(total_steps / steps_per_epoch)
    lr_warmup_steps = int(total_steps * lr_warmup_ratio)

    for folder, (img, rep) in images_repeats.items():
        print("📁 " + folder.replace("/kaggle/input/", "") + (" (Regularization)" if folder in reg else ""))
        print(f"📈 Found {img} images with {rep} repeats, equaling {img * rep} steps.")
    print(f"📉 Divide {pre_steps_per_epoch} steps by {train_batch_size} batch size to get {steps_per_epoch} steps per epoch.")
    if max_train_epochs:
        print(f"🔮 There will be {max_train_epochs} epochs, for around {total_steps} total training steps.")
    else:
        print(f"🔮 There will be {total_steps} steps, divided into {estimated_epochs} epochs and then some.")

    if total_steps > 10000:
        print("💥 Error: Your total steps are too high. You probably made a mistake. Aborting...")
        return

    return True

def create_config():
    global dataset_config_file, config_file, model_file

    if override_config_file:
        config_file = override_config_file
        print(f"\n⭕ Using custom config file {config_file}")
    else:
        config_dict = {
            "network_arguments": {
                "unet_lr": unet_lr,
                "text_encoder_lr": text_encoder_lr if not cache_text_encoder_outputs else 0,
                "network_dim": network_dim,
                "network_alpha": network_alpha,
                "network_module": network_module,
                "network_args": network_args,
                "network_train_unet_only": text_encoder_lr == 0 or cache_text_encoder_outputs,
                "network_weights": continue_from_lora if continue_from_lora else None
            },
            "optimizer_arguments": {
                "learning_rate": unet_lr,
                "lr_scheduler": lr_scheduler,
                "lr_scheduler_num_cycles": lr_scheduler_num_cycles if lr_scheduler == "cosine_with_restarts" else None,
                "lr_scheduler_power": lr_scheduler_power if lr_scheduler == "polynomial" else None,
                "lr_warmup_steps": lr_warmup_steps if lr_scheduler != "constant" else None,
                "optimizer_type": optimizer,
                "optimizer_args": optimizer_args if optimizer_args else None,
            },
            "training_arguments": {
                "pretrained_model_name_or_path": model_file,
                "vae": vae_file,
                "max_train_steps": max_train_steps,
                "max_train_epochs": max_train_epochs,
                "train_batch_size": train_batch_size,
                "seed": 42,
                "max_token_length": 225,
                "xformers": cross_attention == "xformers",
                "sdpa": cross_attention == "sdpa",
                "min_snr_gamma": min_snr_gamma if min_snr_gamma > 0 else None,
                "lowram": LOWRAM,
                "no_half_vae": True,
                "gradient_checkpointing": True,
                "gradient_accumulation_steps": 1,
                "max_data_loader_n_workers": 8,
                "persistent_data_loader_workers": True,
                "mixed_precision": mixed_precision,
                "full_bf16": mixed_precision == "bf16",
                "cache_latents": cache_latents,
                "cache_latents_to_disk": cache_latents_to_drive,
                "cache_text_encoder_outputs": cache_text_encoder_outputs,
                "min_timestep": 0,
                "max_timestep": 1000,
                "prior_loss_weight": 1.0,
                "multires_noise_iterations": 6 if multinoise else None,
                "multires_noise_discount": 0.3 if multinoise else None,
            },
            "saving_arguments": {
                "save_precision": "fp16",
                "save_model_as": "safetensors",
                "save_every_n_epochs": save_every_n_epochs,
                "save_last_n_epochs": keep_only_last_n_epochs,
                "output_name": project_name,
                "output_dir": output_folder,
                "log_prefix": project_name,
                "logging_dir": log_folder,
                "wandb_api_key": wandb_key if wandb_key else None,
                "log_with": "wandb" if wandb_key else None,
            }
        }

        for key in config_dict:
            if isinstance(config_dict[key], dict):
                config_dict[key] = {k: v for k, v in config_dict[key].items() if v is not None}

        with open(config_file, "w") as f:
            f.write(toml.dumps(config_dict))
        print(f"\n📄 Config saved to {config_file}")

    if override_dataset_config_file:
        dataset_config_file = override_dataset_config_file
        print(f"⭕ Using custom dataset config file {dataset_config_file}")
    else:
        dataset_config_dict = {
            "general": {
                "resolution": resolution,
                "shuffle_caption": shuffle_caption and not cache_text_encoder_outputs,
                "keep_tokens": keep_tokens,
                "flip_aug": False,
                "caption_extension": caption_extension,
                "enable_bucket": True,
                "bucket_no_upscale": False,
                "bucket_reso_steps": 64,
                "min_bucket_reso": 256,
                "max_bucket_reso": 4096,
            },
            "datasets": toml.loads(custom_dataset)["datasets"] if custom_dataset else [
                {
                    "subsets": [
                        {
                            "num_repeats": num_repeats,
                            "image_dir": images_folder,
                            "class_tokens": None if caption_extension else project_name
                        }
                    ]
                }
            ]
        }

        for key in dataset_config_dict:
            if isinstance(dataset_config_dict[key], dict):
                dataset_config_dict[key] = {k: v for k, v in dataset_config_dict[key].items() if v is not None}

        with open(dataset_config_file, "w") as f:
            f.write(toml.dumps(dataset_config_dict))
        print(f"📄 Dataset config saved to {dataset_config_file}")

def download_model():
    global old_model_url, model_url, model_file, vae_url, vae_file
    real_model_url = model_url  # There was a reason for having a separate variable but I forgot what it was.

    if real_model_url.startswith("/kaggle/input/") and not os.path.exists(real_model_url):
        print("💥 Error: The custom training model you specified was not found in your Kaggle input.")
        return

    if not load_diffusers:
        if real_model_url.startswith("/kaggle/input/"):
            # Local model, already checked to exist
            model_file = real_model_url
            print(f"📁 Using local model file: {model_file}")
            # Validation
            if model_file.lower().endswith(".safetensors"):
                from safetensors.torch import load_file as load_safetensors
                try:
                    test = load_safetensors(model_file)
                    del test
                except:
                    return False
            elif model_file.lower().endswith(".ckpt"):
                from torch import load as load_ckpt
                try:
                    test = load_ckpt(model_file)
                    del test
                except:
                    return False
            return True

    # Downloadable model
    if load_diffusers:
        if 'huggingface.co' in real_model_url:
            match = re.search(r'huggingface\.co/([^/]+)/([^/]+)', real_model_url)
            if match:
                username = match.group(1)
                model_name = match.group(2)
                model_file = f"{username}/{model_name}"
                fs = HfFileSystem()
                existing_folders = set(fs.ls(model_file, detail=False))
                necessary_folders = ["scheduler", "text_encoder", "text_encoder_2", "tokenizer", "tokenizer_2", "unet", "vae"]
                if all(f"{model_file}/{folder}" in existing_folders for folder in necessary_folders):
                    print("🍃 Diffusers model identified.")  # Will be handled by kohya
                    return True
        raise ValueError("💥 Failed to load Diffusers model. If this model is not Diffusers, have you tried turning it off at the top of this cell?")

    # Define local filename
    if not model_file:
        if real_model_url.lower().endswith((".ckpt", ".safetensors")):
            model_file = os.path.join(root_dir, os.path.basename(real_model_url))
        else:
            model_file = os.path.join(root_dir, "downloaded_model.safetensors")
            if os.path.exists(model_file):
                os.remove(model_file)

    # HuggingFace
    if m := re.search(r"(?:https?://)?(?:www\.)?huggingface\.co/[^/]+/[^/]+/blob", real_model_url):
        real_model_url = real_model_url.replace("blob", "resolve")
    # Civitai
    elif m := re.search(r"(?:https?://)?(?:www\.)?civitai\.com/models/([0-9]+)(/[A-Za-z0-9-_]+)?", real_model_url):
        if m.group(2):
            model_file = os.path.join(root_dir, f"{m.group(2)}.safetensors")
        if m := re.search(r"modelVersionId=([0-9]+)", real_model_url):
            real_model_url = f"https://civitai.com/api/download/models/{m.group(1)}"
        else:
            raise ValueError("💥 optional_custom_training_model contains a civitai link, but the link doesn't include a modelVersionId. You can also right click the download button to copy the direct download link.")

    # Download checkpoint
    !wget "{real_model_url}" -O "{model_file}" --quiet

    # Download VAE
    if not os.path.exists(vae_file):
        !wget "{vae_url}" -O "{vae_file}" --quiet

    # Validation

    if model_file.lower().endswith(".safetensors"):
        from safetensors.torch import load_file as load_safetensors
        try:
            test = load_safetensors(model_file)
            del test
        except:
            new_model_file = os.path.splitext(model_file)[0] + ".ckpt"
            os.rename(model_file, new_model_file)
            model_file = new_model_file
            print(f"Renamed model to {os.path.splitext(model_file)[0]}.ckpt")

    if model_file.lower().endswith(".ckpt"):
        from torch import load as load_ckpt
        try:
            test = load_ckpt(model_file)
            del test
        except:
            return False

    return True

def main():
    global dependencies_installed

    # Create all necessary directories
    for dir in (main_dir, deps_dir, repo_dir, log_folder, images_folder, output_folder, config_folder):
        os.makedirs(dir, exist_ok=True)

    if not validate_dataset():
        return

    if not dependencies_installed:
        print("\n🏭 Installing dependencies...\n")
        t0 = time()
        install_dependencies()
        t1 = time()
        dependencies_installed = True
        print(f"\n✅ Installation finished in {int(t1 - t0)} seconds.")
    else:
        print("\n✅ Dependencies already installed.")

    if old_model_url != model_url or not model_file or not os.path.exists(model_file):
        print("\n🔄 Getting model...")
        if not download_model():
            print("\n💥 Error: The model you specified is invalid or corrupted."
                  "\nIf you're using an URL, please check that the model is accessible without being logged in."
                  "\nYou can try civitai or huggingface URLs, or a path in your Kaggle input starting with /kaggle/input")
            return
        print()
    else:
        print("\n🔄 Model already downloaded.\n")

    create_config()

    print("\n⭐ Starting trainer...\n")
    os.chdir(repo_dir)

    # Execute the training script
    !accelerate launch --quiet --config_file={accelerate_config_file} --num_cpu_threads_per_process=1 train_network_xl_wrapper.py --dataset_config={dataset_config_file} --config_file={config_file}

    # Check for successful completion
    exit_code = os.WEXITSTATUS(os.system('echo $?'))
    if exit_code == 0:
        display(Markdown(f"### ✅ Done! [Download your Lora from /kaggle/working/{output_folder}]\n"
                         "### There will be several files, you should try the latest version (the file with the largest number next to it)"))
    else:
        print("💥 Error during training. Please check the logs for more details.")

main()
```

### **Additional Notes:**

1. **Data Upload:**
   - **Kaggle Datasets:** Upload your dataset and models as Kaggle Datasets and reference them using `/kaggle/input/`. Alternatively, you can upload them directly to the notebook environment.
   
2. **Storage Limitations:**
   - **Kaggle's Storage:** Ensure that your data and models fit within Kaggle's storage limits. Kaggle typically provides up to 20GB of storage per notebook.

3. **Runtime Limits:**
   - **Session Duration:** Kaggle notebooks have a maximum runtime. Ensure your training completes within the allowed time or consider checkpointing your progress.

4. **GPU Availability:**
   - **Hardware Selection:** When setting up your Kaggle notebook, choose the appropriate GPU (e.g., Nvidia Tesla P100) based on your training requirements.

5. **Environment Variables:**
   - **Hugging Face Authentication:** If your model requires authentication, ensure you set up the necessary environment variables or use authentication tokens appropriately.

6. **Debugging:**
   - **Logs:** Use Kaggle's output logs to debug any issues during training. Adjust configurations as needed based on the feedback.

By implementing these changes, you should be able to run the training script seamlessly on Kaggle. Ensure that all dependencies are compatible and that paths correctly reference Kaggle's directory structure.

turns-00052.parquet:28608

8b1b14d0b7ff2ba3d79fcc1b
turn 1/1gpt-4o-2024-08-06EnglishRussia8 words
degenerate_repetitionAbsentFinal dense release
USER
clear
ASSISTANT
Sure, how can I assist you today?

turns-00052.parquet:28609

29259be1c01911119e5d0976
turn 1/1gpt-4o-2024-08-06RussianBrazil362 words
degenerate_repetitionAbsentFinal dense release
USER
придумай и подробно опиши внешность и способности стенда, связанного с манипуляцией телом
ASSISTANT
Название стенда: **"Body Alchemist"** (Телесный Алхимик)

**Внешность:**

"Body Alchemist" обладает человеческим силуэтом, но его облик явно указывает на нечто сверхъестественное. Его кожа переливается металлическим блеском, словно поверхность жидкой ртути, постоянно меняющая форму. В зависимости от текущих способностей его иногда можно увидеть со специфическими узорами на поверхности, которые напоминают татуировки в стиле алхимических символов.

Голова стенда напоминает гладкий шлем без чёткого разделения на лицо и череп, на месте глаз - две вытянутые, сияющие щели, которые могут менять цвет, показывая активное использование способностей. Когда стенд активирует свои способности, по его телу пробегают электрические разряды.

Вдоль позвоночника и на предплечьях имеются рядовые "алхимические пластины" - сегменты, которые могут менять форму и выделяться из тела для выполнения тактических задач. Эти пластины покрыты гравировками, которые при активации светятся мягким светом.

**Способности:**

1. **Трансмутация здоровья:**
   "Body Alchemist" может изменять физическое состояние живых организмов, перекраивая боль и раны. Например, стенд может перемещать травму из одного места в другое, снижая вредный эффект на важные органы или исправляя критические повреждения.

2. **Адаптивная мускулатура:**
   Стенд может временно улучшать физические показатели пользователя, например, усиливать мышцы, увеличивать скорость реакции или расширять подвижность суставов, адаптируя тело под разные боевые условия. Эти изменения кратковременны, но могут быть решающими в бою.

3. **Формовка и слияние тканей:**
   "Body Alchemist" способен временно объединять ткани разных существ или предметов, создавая тактические улучшения самим телам, или совершать фузию с объектами и средами для сокрытия или неожиданной атаки. Например, пользователь может временно "сливаться" с землёй, чтобы избежать обнаружения, или формировать из своей руки острое лезвие из обломка металла.

4. **Манипуляция сенсорными восприятиями:**
   Стенд может выбирать изменения воспринимаемой информации мозгом, вызывая, например, временную слепоту или онемение вражеских частей тела, или изменять ощущение пространства и времени, создавая ложные образы.

5. **Алхимическое восстановление:**
   В случае повреждений тело стенда способно на саморегенерацию, втягивая в себя доступные материальные ресурсы и трансформируя их в свои структурные элементы. Качество и скорость восстановления зависят от доступности и объема ресурсов и могут подходить как для самозащиты, так и для ремонта владельца.

Таким образом, "Body Alchemist" является чрезвычайно гибким и стратегически полезным стендом, с широким спектром тактических и лечебных применений, которые делают его сильным союзником в боевых и критических ситуациях.

turns-00052.parquet:28610

94418e317b7c561c0838a420
turn 1/1gpt-4o-2024-08-06EnglishNorway553 words
degenerate_repetitionAbsentFinal dense release
USER
Create a long detailed text about actor Ted Nugent with the following summary. The title of the video is"Stars Who Are Supporting Trump Till The End". Stick to the information i give you and dont create new information. Focus only on the Subject name and there following Summary. Subject Name: [Ted Nugent] Summary: [Ted Nugent has always supported Trump, stating 'Donald Trump's message sings to Americans because he doesn't play political correct brain dead games.' He offers a hat on his website with the NSFW slogan 'Reelect the mother bleep.']

            Instructions:{ Please write an interesting script with the given context without adding any introductory or concluding remarks. 
            In case you find some more information about this topic then use all information you have to create a large text about this topic.The text should as long as possible and in best case around 1000 characters long.
            Ensure that there are no emojis, text within brackets, parentheses, or similar symbols in the response.
            Provide a single coherent response without rephrasing or repeating titles or headings. Avoid enumerations or bullet points. 
            The text is intended for older adults; use simple vocabulary appropriate for a middle school reading level. Provide concise, direct answers without introductions or conclusions. 
            Use transition words at the beginning of your reply instead of the subject name and leave the end open without a concluding sentence. 
            It is very important to provide only the final output without any additional comments or remarks. 
            Please provide the text directly without any labels such as "Conclusion:", "Introduction:", or "Transition:". Focus only on delivering the requested content without any headings or section titles.
            Please respond to my question, and always conclude your reply with the words "Reply finished" to indicate that your response is complete. Make sure the reply is fully contained before these words and is not cut off prematurely. Thank you.}
         Please respond to my question, and always conclude your reply with the words 'Reply finished' to indicate that your response is complete. Ensure that the text is fully formed without any placeholders such as '[Conclusion]' or '[Placeholder]'. Make sure the reply is fully contained before these words and is not cut off prematurely. Thank you.
ASSISTANT
Since Ted Nugent has always been a vocal supporter of Donald Trump, his admiration for the former president is palpable. Nugent emphatically said, "Donald Trump's message sings to Americans because he doesn't play political correct brain dead games." This statement showcases his disdain for what he perceives as overly sanitized political discourse and highlights why Trump’s message resonates with him. Trump’s straightforward and often unapologetic style aligns with Nugent’s own approach to politics and life, known for his outspoken manner and blunt opinions.

Moreover, Ted Nugent's support isn't limited to verbal endorsements but extends to tangible avenues, such as merchandise on his website. Among available items is a hat emblazoned with the NSFW slogan urging to "Reelect the mother bleep." This product epitomizes Nugent's unfiltered and provocative style, mirroring the sort of rhetoric he admires in Trump.

For Nugent, backing Trump represents a rejection of established political correctness and a call for what he views as genuine, honest communication from leaders. It's clear why Nugent stands firm in his backing of Trump until the very end, seeing him as a kindred spirit who defies conventional political norms. Reply finished.

turns-00052.parquet:28611

8cfe9fbd8b3126a5481e5300
turn 1/1o1-mini-2024-09-12EnglishSouth Korea12484 words
degenerate_repetitionAbsentFinal dense release
USER
// ==UserScript==
// @name         종합 웹 크롤러 7.9.5
// @namespace    http://tampermonkey.net/
// @version      7.9.5
// @description  고급 기능을 갖춘 웹 크롤러로 검색 결과를 실시간으로 수집하고 표시 (동시 크롤링, 번역 기능, UI 개선 등)
// @author       YourName
// @match        *://www.google.com/search*
// @match        *://www.bing.com/search*
// @match        *://search.daum.net/search*
// @match        *://search.naver.com/search*
// @match        *://search.yahoo.com/search*
// @match        *://duckduckgo.com/*
// @match        *://www.ecosia.org/search*
// @match        *://*.onion/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_notification
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @require      https://cdn.jsdelivr.net/npm/chart.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/i18next/21.6.15/i18next.min.js
// ==/UserScript==

(function() {
    'use strict';

    // CSS 스타일 추가 (UI 대폭 개선)
    GM_addStyle(`
        /* 컨테이너 스타일 */
        #crawler-container {
            position: fixed;
            top: 20px;
            right: 20px;
            background-color: #2c3e50;
            border: 2px solid #1abc9c;
            padding: 20px;
            z-index: 10000;
            width: 800px;
            max-height: 85vh;
            overflow: hidden;
            box-shadow: 0 8px 30px rgba(0,0,0,0.5);
            border-radius: 12px;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            color: #ecf0f1;
            display: flex;
            flex-direction: column;
            transition: all 0.3s ease;
        }

        /* 헤더 스타일 */
        #crawler-container header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }

        #crawler-container .title {
            font-size: 20px;
            font-weight: bold;
        }

        #crawler-container .close-btn {
            cursor: pointer;
            font-size: 24px;
            color: #e74c3c;
            transition: color 0.3s;
        }

        #crawler-container .close-btn:hover {
            color: #c0392b;
        }

        /* 탭 스타일 */
        #crawler-container .tabs {
            display: flex;
            border-bottom: 2px solid #1abc9c;
            margin-bottom: 15px;
            overflow-x: auto;
        }

        #crawler-container .tab {
            flex: 1;
            text-align: center;
            padding: 10px 0;
            cursor: pointer;
            background-color: #34495e;
            border-right: 1px solid #1abc9c;
            transition: background-color 0.3s, color 0.3s;
            white-space: nowrap;
        }

        #crawler-container .tab:last-child {
            border-right: none;
        }

        #crawler-container .tab.active {
            background-color: #1abc9c;
            color: #2c3e50;
            font-weight: bold;
        }

        /* 탭 콘텐츠 스타일 */
        #crawler-container .tab-content {
            display: none;
            flex: 1;
            overflow-y: auto;
        }

        #crawler-container .tab-content.active {
            display: block;
        }

        /* 버튼 스타일 */
        #crawler-container .controls button {
            background-color: #1abc9c;
            border: none;
            color: #2c3e50;
            padding: 10px 16px;
            margin: 6px 3px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s, transform 0.2s;
            font-size: 14px;
            display: flex;
            align-items: center;
        }

        #crawler-container .controls button:hover {
            background-color: #16a085;
            transform: translateY(-2px);
        }

        #crawler-container .controls button i {
            margin-right: 6px;
        }

        /* 체크박스 및 라벨 스타일 */
        #crawler-container .options label,
        #crawler-container .filters label,
        #crawler-container .exclude-domains label,
        #crawler-container .advanced-options label {
            display: flex;
            align-items: center;
            margin: 10px 0;
            font-size: 14px;
        }

        #crawler-container .options input[type="checkbox"] {
            margin-right: 8px;
            transform: scale(1.2);
        }

        /* 스위치 스타일 */
        .switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
            margin-left: 10px;
        }

        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .switch .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: 0.4s;
            border-radius: 24px;
        }

        .switch .slider:before {
            position: absolute;
            content: "";
            height: 18px;
            width: 18px;
            left: 3px;
            bottom: 3px;
            background-color: white;
            transition: 0.4s;
            border-radius: 50%;
        }

        .switch input:checked + .slider {
            background-color: #1abc9c;
        }

        .switch input:checked + .slider:before {
            transform: translateX(26px);
        }

        /* 입력 필드 스타일 */
        #crawler-container input[type="text"],
        #crawler-container input[type="number"] {
            width: 100%;
            padding: 8px;
            margin: 6px 0;
            border: 1px solid #bdc3c7;
            border-radius: 4px;
            font-size: 14px;
        }

        /* 테이블 스타일 */
        #resultsTable {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }

        #resultsTable th, #resultsTable td {
            border: 1px solid #95a5a6;
            padding: 10px;
            text-align: left;
            font-size: 13px;
        }

        #resultsTable th {
            background-color: #1abc9c;
            color: #2c3e50;
        }

        #resultsTable tbody tr:nth-child(even) {
            background-color: #34495e;
        }

        /* 반응형 디자인 */
        @media (max-width: 900px) {
            #crawler-container {
                width: 90%;
                right: 5%;
                padding: 15px;
            }

            #crawler-container .controls button {
                padding: 8px 12px;
                font-size: 13px;
            }

            #crawler-container .title {
                font-size: 18px;
            }
        }

        /* Papago iframe 스타일 */
        #papagoIframe {
            width: 100%;
            height: 100%;
            border: none;
        }

        /* 팝업 스타일 */
        .search-engines-popup, .manage-darkweb-popup {
            position: absolute;
            background-color: #2c3e50;
            border: 1px solid #1abc9c;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            max-height: 350px;
            overflow-y: auto;
            z-index: 10001;
        }

        .search-engines-popup button, .manage-darkweb-popup button {
            width: 100%;
            text-align: left;
            margin: 6px 0;
            background-color: #34495e;
            color: #ecf0f1;
            border: none;
            padding: 10px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 14px;
        }

        .search-engines-popup button:hover, .manage-darkweb-popup button:hover {
            background-color: #1abc9c;
            color: #2c3e50;
        }

        /* 로그 스타일 */
        #log {
            background-color: #34495e;
            padding: 10px;
            border-radius: 5px;
            max-height: 200px;
            overflow-y: auto;
            font-size: 13px;
            line-height: 1.4;
        }

        /* 차트 컨테이너 */
        .chart-container {
            margin-top: 15px;
            height: 250px;
        }

        /* HTML 디스플레이 스타일 */
        .html-display iframe {
            width: 100%;
            height: 500px;
            border: none;
            border-radius: 5px;
        }

        /* Papago 디스플레이 스타일 */
        .papago-display {
            width: 100%;
            height: 500px;
            position: relative;
        }

        /* 다크웹 엔진 목록 스타일 */
        #darkwebEnginesList li {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
        }

        #darkwebEnginesList li span {
            flex: 1;
            margin-right: 10px;
            word-break: break-all;
        }

        #darkwebEnginesList li button {
            background-color: #e74c3c;
            border: none;
            color: #fff;
            padding: 6px 10px;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        #darkwebEnginesList li button:hover {
            background-color: #c0392b;
        }

        /* 예외 도메인 리스트 스타일 */
        .exclude-domains ul {
            list-style: none;
            padding: 0;
            margin-top: 10px;
        }

        .exclude-domains ul li {
            background-color: #34495e;
            padding: 6px 10px;
            border-radius: 4px;
            margin-bottom: 6px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .exclude-domains ul li button {
            background-color: #e74c3c;
            border: none;
            color: #fff;
            padding: 4px 8px;
            border-radius: 3px;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 12px;
        }

        .exclude-domains ul li button:hover {
            background-color: #c0392b;
        }

        /* Search Engines Direct UI */
        #search-engines-direct {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 15px;
        }

        #search-engines-direct button {
            flex: 1 1 45%;
            background-color: #1abc9c;
            border: none;
            color: #2c3e50;
            padding: 8px 12px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 14px;
        }

        #search-engines-direct button:hover {
            background-color: #16a085;
        }

        /* Preview Popup */
        .preview-popup {
            position: absolute;
            background-color: #2c3e50;
            border: 1px solid #1abc9c;
            padding: 10px;
            border-radius: 6px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            width: 300px;
            height: 150px;
            z-index: 10002;
            display: none;
        }
    `);

    // UI 생성 (대폭 개선된 디자인)
    const container = document.createElement('div');
    container.id = 'crawler-container';
    container.innerHTML = `
        <header>
            <span class="title">🔥 웹 크롤러 7.9.5</span>
            <span class="close-btn" title="닫기">&times;</span>
        </header>
        <div class="tabs">
            <div class="tab active" data-tab="settings">설정</div>
            <div class="tab" data-tab="results">결과</div>
            <div class="tab" data-tab="logs">로그</div>
            <div class="tab" data-tab="site-filters">사이트 필터</div>
            <div class="tab" data-tab="darkweb">다크웹</div>
            <div class="tab" data-tab="html-display">HTML</div>
            <div class="tab" data-tab="papago">Papago</div>
            <div class="tab" data-tab="search-engines">검색 엔진</div>
        </div>
        <div class="tab-content active" id="settings">
            <!-- 제어 버튼 -->
            <div class="controls">
                <button id="startBtn" title="크롤링 시작"><i>▶️</i> 시작</button>
                <button id="pauseBtn" class="pause-btn" title="크롤링 일시정지/재개"><i>⏸️</i> 일시정지</button>
                <button id="stopBtn" title="크롤링 중지"><i>⏹️</i> 중지</button>
                <button id="resetBtn" title="크롤링 초기화"><i>🔄</i> 초기화</button>
                <button id="exportBtn" class="export-btn" title="결과 내보내기"><i>💾</i> 내보내기</button>
                <button id="searchEnginesBtn" class="search-engines-btn" title="검색 엔진 바로가기"><i>🔍</i> 검색 엔진</button>
            </div>
            <!-- 옵션 섹션 -->
            <div class="options">
                <label><input type="checkbox" id="toggleHTML" checked> HTML 가져오기</label>
                <label><input type="checkbox" id="toggleImages" checked> 이미지 가져오기</label>
                <label><input type="checkbox" id="toggleAds" checked> 광고 제외</label>
                <label>엄격 사이트 필터링
                    <label class="switch">
                        <input type="checkbox" id="strictSiteFilter">
                        <span class="slider"></span>
                    </label>
                </label>
                <label>필터링 기능 활성화
                    <label class="switch">
                        <input type="checkbox" id="filteringToggle" checked>
                        <span class="slider"></span>
                    </label>
                </label>
            </div>
            <!-- 필터 섹션 -->
            <div class="filters">
                <label for="filterInput">필터 단어 (정규식, 쉼표로 구분):</label>
                <input type="text" id="filterInput" placeholder="예: 기부, 광고, /promo/i">
            </div>
            <!-- 도메인 제외 섹션 -->
            <div class="exclude-domains">
                <label for="excludeDomainsInput">제외할 도메인 (쉼표로 구분):</label>
                <input type="text" id="excludeDomainsInput" placeholder="예: facebook.com, youtube.com">
                <ul id="excludeDomainsList"></ul>
            </div>
            <!-- 고급 옵션 섹션 -->
            <div class="advanced-options">
                <label for="duplicateThreshold">중복 허용 횟수:</label>
                <input type="number" id="duplicateThreshold" value="5" min="1" style="width: 80px;">
            </div>
            <!-- 제어 옵션 섹션 -->
            <div class="controls">
                <label for="retryAttempts">재시도 횟수:</label>
                <input type="number" id="retryAttempts" value="10" min="1" style="width: 80px;">
                <label for="retryInterval">재시도 간격 (ms):</label>
                <input type="number" id="retryInterval" value="100" min="100" style="width: 80px;">
            </div>
            <!-- 다크웹 검색 엔진 토글 -->
            <div class="controls">
                <label>다크웹 검색 엔진 사용
                    <label class="switch">
                        <input type="checkbox" id="darkwebToggle" checked>
                        <span class="slider"></span>
                    </label>
                </label>
            </div>
            <!-- 진행률 표시 섹션 -->
            <div class="progress-section">
                <progress id="progressBar" value="0" max="100" style="width: 100%; height: 20px;"></progress>
                <span id="progressText">페이지 0</span>
            </div>
            <!-- 상태 정보 섹션 -->
            <div class="status">
                현재 페이지: <span id="currentPage">0</span>
                <br>
                방문된 사이트: <span id="visitedCount">0</span>
                <br>
                경과 시간: <span id="elapsedTime">00:00:00</span>
                <br>
                예상 남은 시간: <span id="remainingTime">00:00:00</span>
            </div>
            <!-- 차트 섹션 -->
            <div class="chart-container">
                <canvas id="crawlChart"></canvas>
            </div>
        </div>
        <div class="tab-content" id="results">
            <div style="margin-top:15px; overflow: auto;">
                <table id="resultsTable">
                    <thead>
                        <tr>
                            <th>사이트 이름</th>
                            <th>주소</th>
                            <th>설명</th>
                            <th>이미지</th>
                            <th>HTML 링크</th>
                            <th>페이지 로딩 시간 (ms)</th>
                        </tr>
                    </thead>
                    <tbody id="resultsBody">
                    </tbody>
                </table>
            </div>
        </div>
        <div class="tab-content" id="logs">
            <div class="log" id="log">
                <strong>로그:</strong><br>
            </div>
        </div>
        <div class="tab-content" id="site-filters">
            <div class="site-filters">
                <label for="siteFilterInput">사이트 필터링 (정규식, 쉼표로 구분):</label>
                <input type="text" id="siteFilterInput" placeholder="예: .*example\\.com.*, /^https?:\\/\\/.*\\.example\\.org$/">
            </div>
        </div>
        <div class="tab-content" id="darkweb">
            <div class="darkweb-search">
                <label>다크웹 검색 엔진 추가/관리</label>
                <button class="add-darkweb-engine-btn" title="다크웹 검색 엔진 추가"><i>➕</i> 추가</button>
                <ul id="darkwebEnginesList">
                    <!-- 다크웹 검색 엔진 목록 -->
                </ul>
            </div>
        </div>
        <div class="tab-content" id="html-display">
            <div class="html-display"></div>
        </div>
        <div class="tab-content" id="papago">
            <div class="papago-display">
                <iframe id="papagoIframe" src="https://papago.naver.com/"></iframe>
            </div>
        </div>
        <div class="tab-content" id="search-engines">
            <div class="search-engines-direct" id="search-engines-direct">
                <!-- 검색 엔진 바로가기 버튼 추가 -->
            </div>
        </div>
    `;
    document.body.appendChild(container);

    // 드래그 가능하게 설정
    (function makeDraggable(elmnt) {
        const header = elmnt.querySelector('header');
        const closeBtn = elmnt.querySelector('.close-btn');
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = dragMouseDown;

        closeBtn.onclick = () => {
            elmnt.style.display = 'none';
        }

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
            elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    })(container);

    // 탭 기능 구현
    (function setupTabs() {
        const tabs = container.querySelectorAll('.tab');
        const tabContents = container.querySelectorAll('.tab-content');

        tabs.forEach(tab => {
            tab.addEventListener('click', () => {
                tabs.forEach(t => t.classList.remove('active'));
                tab.classList.add('active');

                const target = tab.getAttribute('data-tab');
                tabContents.forEach(content => {
                    if(content.id === target){
                        content.classList.add('active');
                    } else {
                        content.classList.remove('active');
                    }
                });
            });
        });
    })();

    // 변수 초기화
    let isRunning = false;
    let isPaused = false;
    let currentPage = 1;
    let results = [];
    let searchQuery = '';
    let searchEngine = '';
    let visitedLinks = new Map(); // 도메인별 방문 횟수 추적
    let logMessages = [];
    let filterPatterns = [];
    let excludeDomains = [];
    let siteFilterPatterns = [];
    let duplicateThreshold = 5; // 중복 허용 횟수
    let retryAttempts = 10; // 재시도 횟수
    let retryInterval = 100; // 재시도 간격 100ms으로 설정
    let startTime = null;
    let elapsedInterval = null;
    let crawlChart = null;
    let crawlData = {
        labels: [],  // 도메인 이름
        datasets: [{
            label: '방문 횟수',
            data: [],
            backgroundColor: 'rgba(46, 204, 113, 0.6)',
            borderColor: 'rgba(46, 204, 113, 1)',
            borderWidth: 1
        }]
    };
    let filteringEnabled = true; // 필터링 활성화 여부
    let concurrentRequests = 40; // 동시 요청 수 40으로 설정
    let currentRequests = 0; // 현재 요청 수
    let requestQueue = []; // 요청 대기열
    let translateLanguage = 'en'; // 기본 번역 언어
    let darkwebEnabled = true; // 다크웹 검색 엔진 사용 여부

    // 다크웹 검색 엔진 목록 (기본 엔진 추가 및 확장)
    const defaultDarkwebEngines = [
        { name: 'Torch', url: 'http://xmh57jrzrnw6insl.onion/' },
        { name: 'Ahmia', url: 'http://msydqstlz2kzerdg.onion/' },
        { name: 'Haystak', url: 'http://haystakvxad7wbk5.onion/' },
        { name: 'Not Evil', url: 'http://hss3uro2hsxfogfq.onion/' },
        { name: 'Phobos', url: 'http://phobos6uj4abm4pf.onion/' },
        { name: 'Kilos', url: 'http://kilos7wbfn3ptgw.onion/' },
        { name: 'Candle', url: 'http://zg6ak7qqfhbkbppx.onion/' },
        { name: 'DarkSearch', url: 'http://darksearch3w3nrc.onion/' },
        { name: 'Dark Falcon', url: 'http://darkfalconphp5fh.onion/' },
        { name: 'SearXNG', url: 'http://searxngueyn5qzuv.onion/' },
        { name: 'DuckDuckGo (onion)', url: 'http://duckduckgogj7ay7.onion/' },
        { name: 'Phobos 2', url: 'http://phobos2uj4abm4pf.onion/' },
        { name: 'IntelligenceX', url: 'http://intelligencexmh5v.onion/' },
        { name: 'Silk Road 3.1', url: 'http://silkroad31lo5ya.onion/' },
        { name: 'Fresh Onions', url: 'http://rx7hmtf7k7xk6ked.onion/' },
        // 추가 엔진 가능
        { name: 'Dark Web Link', url: 'http://darkweblink5wqk.onion/' },
        { name: 'Tor Tracker', url: 'http://tortracker5ujzra.onion/' },
        { name: 'Deep Web Search', url: 'http://deepwebsearch6b6x.onion/' },
        { name: 'Onion Monitor', url: 'http://onionmonitor3c7v.onion/' },
        { name: 'Hidden Wiki', url: 'http://hiddenwiki5z35.onion/' },
        { name: 'TorEX', url: 'http://torexk3wd2buz.onion/' },
        { name: 'BlackBook', url: 'http://blackbook51uv.onion/' },
        { name: 'Taboo Search', url: 'http://taboosearch37b.onion/' },
        { name: 'The Pirate Bay (onion)', url: 'http://uj3wazyk5u4hnvtk.onion/' },
        // 더 많은 다크웹 검색 엔진을 추가할 수 있습니다.
    ];

    // 버튼 및 요소 가져오기
    const startBtn = document.getElementById('startBtn');
    const pauseBtn = document.getElementById('pauseBtn');
    const stopBtn = document.getElementById('stopBtn');
    const resetBtn = document.getElementById('resetBtn');
    const exportBtn = document.getElementById('exportBtn');
    const searchEnginesBtn = document.getElementById('searchEnginesBtn');
    const progressBar = document.getElementById('progressBar');
    const progressText = document.getElementById('progressText');
    const resultsBody = document.getElementById('resultsBody');
    const logContainer = document.getElementById('log');
    const currentPageElem = document.getElementById('currentPage');
    const toggleHTML = document.getElementById('toggleHTML');
    const toggleImages = document.getElementById('toggleImages');
    const toggleAds = document.getElementById('toggleAds');
    const filterInput = document.getElementById('filterInput');
    const excludeDomainsInput = document.getElementById('excludeDomainsInput');
    const excludeDomainsList = document.getElementById('excludeDomainsList');
    const siteFilterInput = document.getElementById('siteFilterInput');
    const duplicateThresholdInput = document.getElementById('duplicateThreshold');
    const retryAttemptsInput = document.getElementById('retryAttempts');
    const retryIntervalInput = document.getElementById('retryInterval');
    const strictSiteFilter = document.getElementById('strictSiteFilter');
    const visitedCountElem = document.getElementById('visitedCount');
    const elapsedTimeElem = document.getElementById('elapsedTime');
    const remainingTimeElem = document.getElementById('remainingTime');
    const crawlChartCtx = document.getElementById('crawlChart').getContext('2d');
    const filteringToggle = document.getElementById('filteringToggle');
    const darkwebEnginesList = document.getElementById('darkwebEnginesList');
    const addDarkwebEngineBtn = container.querySelector('.add-darkweb-engine-btn');
    const darkwebToggle = document.getElementById('darkwebToggle');
    const papagoIframe = document.getElementById('papagoIframe');
    const searchEnginesDirect = document.getElementById('search-engines-direct');

    // 이벤트 리스너
    startBtn.addEventListener('click', startCrawling);
    pauseBtn.addEventListener('click', togglePause);
    stopBtn.addEventListener('click', stopCrawling);
    resetBtn.addEventListener('click', resetCrawling);
    exportBtn.addEventListener('click', exportResults);
    searchEnginesBtn.addEventListener('click', openSearchEngines);
    filterInput.addEventListener('input', updateFilterPatterns);
    excludeDomainsInput.addEventListener('keydown', function(e) {
        if (e.key === 'Enter') {
            addExcludeDomain();
        }
    });
    addDarkwebEngineBtn.addEventListener('click', addDarkwebEngine);
    siteFilterInput.addEventListener('input', updateSiteFilterPatterns);
    duplicateThresholdInput.addEventListener('change', updateDuplicateThreshold);
    retryAttemptsInput.addEventListener('change', updateRetryAttempts);
    retryIntervalInput.addEventListener('change', updateRetryInterval);
    strictSiteFilter.addEventListener('change', updateStrictSiteFilter);
    filteringToggle.addEventListener('change', toggleFiltering);
    darkwebToggle.addEventListener('change', toggleDarkweb);
    papagoIframe.addEventListener('load', () => {
        log('🌐 Papago 번역기 로드 완료.');
    });

    // 페이지 새로 고침 시 크롤링 초기화
    window.addEventListener('beforeunload', () => {
        resetCrawling();
    });

    // 초기화 로드
    loadLogs();
    loadCrawlData();
    loadDarkwebEngines();
    loadExcludeDomains();
    loadSearchEnginesDirect();

    // 차트 초기화
    function initializeChart() {
        crawlChart = new Chart(crawlChartCtx, {
            type: 'bar',
            data: crawlData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: { beginAtZero: true },
                    x: { ticks: { autoSkip: false } }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });
    }
    initializeChart();

    // 로깅 함수
    function log(message) {
        const time = new Date().toLocaleTimeString();
        const entry = `[${time}] ${message}`;
        logMessages.push(entry);
        logContainer.innerHTML += `${entry}<br>`;
        logContainer.scrollTop = logContainer.scrollHeight;
        // 저장
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
    }

    function loadLogs() {
        const storedLogs = GM_getValue('crawlerLogs', []);
        if (storedLogs.length > 0) {
            logMessages = JSON.parse(storedLogs);
            logContainer.innerHTML = `<strong>로그:</strong><br>` + logMessages.join('<br>');
            logContainer.scrollTop = logContainer.scrollHeight;
        }
    }

    function updateFilterPatterns() {
        if (!filteringEnabled) {
            filterPatterns = [];
            log(`🛠 필터링 기능 비활성화됨.`);
            return;
        }
        const input = filterInput.value.trim();
        filterPatterns = input ? input.split(',').map(word => {
            word = word.trim();
            if (word.startsWith('/') && word.endsWith('/')) {
                try {
                    return new RegExp(word.slice(1, -1), 'i');
                } catch (e) {
                    log(`⚠️ 유효하지 않은 정규식: ${word}`);
                    return null;
                }
            } else {
                return new RegExp(word, 'i');
            }
        }).filter(regex => regex !== null) : [];
        log(`🛠 필터 패턴 업데이트: [${filterPatterns.map(r => r.toString()).join(', ')}]`);
    }

    function updateExcludeDomains() {
        // 이 함수는 제외 도메인이 추가될 때마다 호출됨
        log(`🚫 제외 도메인 업데이트 완료.`);
    }

    function addExcludeDomain() {
        const domain = excludeDomainsInput.value.trim().toLowerCase();
        if (domain && !excludeDomains.includes(domain)) {
            excludeDomains.push(domain);
            renderExcludeDomains();
            excludeDomainsInput.value = '';
            log(`🚫 제외 도메인 추가: ${domain}`);
        }
    }

    function renderExcludeDomains() {
        excludeDomainsList.innerHTML = '';
        excludeDomains.forEach((domain, index) => {
            const li = document.createElement('li');
            li.innerHTML = `
                <span>${escapeHTML(domain)}</span>
                <button data-index="${index}">✖️</button>
            `;
            excludeDomainsList.appendChild(li);
        });
        // 삭제 버튼 이벤트 리스너
        excludeDomainsList.querySelectorAll('button').forEach(btn => {
            btn.addEventListener('click', () => {
                const index = parseInt(btn.getAttribute('data-index'));
                if (!isNaN(index)) {
                    const removed = excludeDomains.splice(index, 1);
                    renderExcludeDomains();
                    log(`🚫 제외 도메인 제거: ${removed}`);
                }
            });
        });
    }

    function loadExcludeDomains() {
        const storedDomains = GM_getValue('excludeDomains', []);
        excludeDomains = storedDomains.length > 0 ? storedDomains : [];
        renderExcludeDomains();
    }

    function updateSiteFilterPatterns() {
        const input = siteFilterInput.value.trim();
        siteFilterPatterns = input ? input.split(',').map(pattern => {
            pattern = pattern.trim();
            if (pattern.startsWith('/') && pattern.endsWith('/')) {
                try {
                    return new RegExp(pattern.slice(1, -1), 'i');
                } catch (e) {
                    log(`⚠️ 유효하지 않은 사이트 필터 정규식: ${pattern}`);
                    return null;
                }
            } else {
                return new RegExp(pattern, 'i');
            }
        }).filter(regex => regex !== null) : [];
        log(`🛠 사이트 필터 패턴 업데이트: [${siteFilterPatterns.map(r => r.toString()).join(', ')}]`);
    }

    function updateDuplicateThreshold() {
        const value = parseInt(duplicateThresholdInput.value);
        duplicateThreshold = isNaN(value) || value < 1 ? 5 : value;
        log(`🔄 중복 허용 횟수 설정: ${duplicateThreshold}`);
    }

    function updateRetryAttempts() {
        const value = parseInt(retryAttemptsInput.value);
        retryAttempts = isNaN(value) || value < 1 ? 10 : value;
        log(`🔄 재시도 횟수 설정: ${retryAttempts}`);
    }

    function updateRetryInterval() {
        const value = parseInt(retryIntervalInput.value);
        retryInterval = isNaN(value) || value < 100 ? 100 : value; // 최소 값 100ms으로 설정
        log(`🔄 재시도 간격 설정: ${retryInterval}ms`);
    }

    function updateStrictSiteFilter() {
        const isStrict = strictSiteFilter.checked;
        log(`🔄 엄격 사이트 필터링 설정: ${isStrict ? 'ON' : 'OFF'}`);
    }

    function toggleFiltering() {
        filteringEnabled = filteringToggle.checked;
        if (filteringEnabled) {
            log('🔄 필터링 기능 활성화됨.');
            updateFilterPatterns();
        } else {
            filterPatterns = [];
            log('🔄 필터링 기능 비활성화됨.');
        }
    }

    function toggleDarkweb() {
        darkwebEnabled = darkwebToggle.checked;
        log(`🔄 다크웹 검색 엔진 사용 설정: ${darkwebEnabled ? 'ON' : 'OFF'}`);
    }

    function getSearchQueryAndEngine() {
        const url = window.location.href;
        if (url.includes('google.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'google';
        } else if (url.includes('bing.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'bing';
        } else if (url.includes('daum.net/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'daum';
        } else if (url.includes('search.naver.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('query') || '';
            searchEngine = 'naver';
        } else if (url.includes('search.yahoo.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('p') || '';
            searchEngine = 'yahoo';
        } else if (url.includes('duckduckgo.com/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'duckduckgo';
        } else if (url.includes('ecosia.org/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'ecosia';
        } else if (url.includes('.onion')) {
            // 다크웹 검색 엔진 중 하나로 설정
            searchEngine = 'darkweb';
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            if (!searchQuery) {
                searchQuery = prompt('다크웹 검색어를 입력하세요:');
            }
        } else {
            searchQuery = '';
            searchEngine = '';
        }
        log(`🔍 검색 엔진: ${searchEngine}, 검색어: "${searchQuery}"`);
    }

    async function startCrawling() {
        if (isRunning) {
            alert('이미 크롤링이 진행 중입니다.');
            return;
        }
        getSearchQueryAndEngine();
        if (!searchQuery) {
            alert('현재 페이지에서 검색어를 찾을 수 없습니다.');
            log('⚠️ 검색어 미입력 또는 인식 실패.');
            return;
        }
        isRunning = true;
        isPaused = false;
        currentPage = 1;
        results = [];
        visitedLinks.clear();
        resultsBody.innerHTML = '';
        visitedCountElem.innerText = '0';
        elapsedTimeElem.innerText = '00:00:00';
        remainingTimeElem.innerText = '00:00:00';
        logMessages = [];
        logContainer.innerHTML = `<strong>로그:</strong><br>`;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
        log('✅ 크롤링을 시작합니다.');
        updateDuplicateThreshold();
        updateRetryAttempts();
        updateRetryInterval();
        await enqueueCrawlPages(concurrentRequests); // 동시 요청 수 40으로 설정
        startTime = Date.now();
        elapsedInterval = setInterval(updateElapsedTime, 1000);
    }

    function togglePause() {
        if (!isRunning) {
            alert('일시정지/재개할 크롤링이 없습니다.');
            return;
        }
        isPaused = !isPaused;
        if (isPaused) {
            log('⏸️ 크롤링을 일시정지하였습니다.');
            pauseBtn.innerHTML = '<i>▶️</i> 재개';
        } else {
            log('▶️ 크롤링을 재개하였습니다.');
            pauseBtn.innerHTML = '<i>⏸️</i> 일시정지';
            processQueue();
        }
    }

    function stopCrawling() {
        if (!isRunning) {
            alert('중지할 크롤링이 없습니다.');
            return;
        }
        isRunning = false;
        isPaused = false;
        pauseBtn.innerHTML = '<i>⏸️</i> 일시정지';
        clearInterval(elapsedInterval);
        updateProgress();
        log('🚫 크롤링을 중지하였습니다.');
        GM_notification({
            text: '크롤링이 중지되었습니다.',
            title: '웹 크롤러 7.9.5',
            timeout: 3000
        });
    }

    async function enqueueCrawlPages(limit) {
        for (let i = 0; i < limit; i++) {
            queueCrawlPage();
        }
    }

    function queueCrawlPage() {
        if (!isRunning || isPaused) return;
        if (currentRequests >= concurrentRequests) return;

        // Assign a unique page number before starting the crawl
        const pageToCrawl = currentPage++;
        currentPageElem.innerText = currentPage;

        currentRequests++;
        crawlPage(pageToCrawl).then(() => {
            currentRequests--;
            queueCrawlPage();
        });
    }

    async function crawlPage(page) {
        if (!isRunning || isPaused) {
            return;
        }

        // 검색 엔진에 따른 URL 구성
        let url = '';
        if (searchEngine === 'google') {
            url = `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}&start=${(page-1)*10}`;
        } else if (searchEngine === 'bing') {
            url = `https://www.bing.com/search?q=${encodeURIComponent(searchQuery)}&first=${(page-1)*10 +1}`;
        } else if (searchEngine === 'daum') {
            url = `https://search.daum.net/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'naver') {
            url = `https://search.naver.com/search.naver?query=${encodeURIComponent(searchQuery)}&start=${(page-1)*10 +1}`;
        } else if (searchEngine === 'yahoo') {
            url = `https://search.yahoo.com/search?p=${encodeURIComponent(searchQuery)}&b=${(page-1)*10 +1}`;
        } else if (searchEngine === 'duckduckgo') {
            url = `https://duckduckgo.com/html/?q=${encodeURIComponent(searchQuery)}&s=${(page-1)*10}`;
        } else if (searchEngine === 'ecosia') {
            url = `https://www.ecosia.org/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'darkweb') {
            if (!darkwebEnabled) {
                log('🛑 다크웹 검색 엔진이 비활성화되어 있습니다.');
                isRunning = false;
                return;
            }
            const darkwebEngines = getDarkwebEngines();
            if (darkwebEngines.length === 0) {
                log('⚠️ 다크웹 검색 엔진이 설정되지 않았습니다.');
                isRunning = false;
                return;
            }
            const randomEngine = darkwebEngines[Math.floor(Math.random() * darkwebEngines.length)];
            url = `${randomEngine.url}/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else {
            alert('지원하지 않는 검색 엔진입니다.');
            log('❌ 지원하지 않는 검색 엔진.');
            isRunning = false;
            return;
        }

        log(`📄 페이지 ${page} 크롤링 중: ${url}`);

        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                headers: {
                    "User-Agent": navigator.userAgent,
                    "Accept": "text/html"
                },
                onload: async function(response) {
                    if (response.status !== 200) {
                        log(`❌ 페이지 ${page} 로드 실패: 상태 ${response.status}`);
                        await handleCrawlError(page, retryAttempts);
                        resolve();
                        return;
                    }

                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');
                    let items = [];
                    if (searchEngine === 'google') {
                        items = doc.querySelectorAll('div.g');
                    } else if (searchEngine === 'bing') {
                        items = doc.querySelectorAll('li.b_algo');
                    } else if (searchEngine === 'daum') {
                        items = doc.querySelectorAll('div.wrap_cont');
                    } else if (searchEngine === 'naver') {
                        items = doc.querySelectorAll('div.basic1');
                    } else if (searchEngine === 'yahoo') {
                        items = doc.querySelectorAll('div.compTitle');
                    } else if (searchEngine === 'duckduckgo') {
                        items = doc.querySelectorAll('div.result');
                    } else if (searchEngine === 'ecosia') {
                        items = doc.querySelectorAll('div.result');
                    } else if (searchEngine === 'darkweb') {
                        // 다크웹 검색 엔진 중 하나로 설정 (예: Torch 기준)
                        items = doc.querySelectorAll('.result'); // 실제 검색 엔진에 맞게 조정 필요
                    }

                    // 만약 검색 결과가 없다면 크롤링 종료
                    if (items.length === 0) {
                        log('🔍 더 이상 크롤링할 페이지가 없습니다. 크롤링을 종료합니다.');
                        isRunning = false;
                        isPaused = false;
                        pauseBtn.innerHTML = '<i>⏸️</i> 일시정지';
                        clearInterval(elapsedInterval);
                        updateProgress();
                        GM_notification({
                            text: '크롤링이 완료되었습니다.',
                            title: '웹 크롤러 7.9.5',
                            timeout: 3000
                        });
                        resolve();
                        return;
                    }

                    log(`📊 페이지 ${page}에서 ${items.length}개 항목 발견.`);

                    // 병렬 처리: Promise.all을 이용하여 비동기적으로 처리
                    const itemPromises = Array.from(items).map(async (item) => {
                        let title = 'N/A';
                        let link = 'N/A';
                        let description = 'N/A';
                        let image = 'N/A';
                        let htmlLink = '비활성화';
                        let pageLoadTime = 'N/A';

                        // 각 검색 엔진에 따른 데이터 추출
                        if (searchEngine === 'google') {
                            const titleElem = item.querySelector('h3');
                            const linkElem = item.querySelector('a');
                            const descElem = item.querySelector('.IsZvec') || item.querySelector('.VwiC3b');
                            if (titleElem) title = titleElem.innerText;
                            if (linkElem) link = linkElem.href;
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'bing') {
                            const titleElem = item.querySelector('h2 a');
                            const descElem = item.querySelector('p');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'daum') {
                            const titleElem = item.querySelector('a.link_fax');
                            const descElem = item.querySelector('div.cont_inner');
                            if (titleElem) {
                                title = titleElem.innerText.trim();
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText.trim();

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'naver') {
                            const titleElem = item.querySelector('a.tit');
                            const descElem = item.querySelector('div.desc');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img.thumb');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'yahoo') {
                            const titleElem = item.querySelector('h3');
                            const linkElem = item.querySelector('a');
                            const descElem = item.querySelector('div.compText');
                            if (titleElem) title = titleElem.innerText;
                            if (linkElem) link = linkElem.href;
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'duckduckgo') {
                            const titleElem = item.querySelector('a.result__a');
                            const descElem = item.querySelector('a.result__snippet');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img.result__thumbnail');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'ecosia') {
                            const titleElem = item.querySelector('a.result-title');
                            const descElem = item.querySelector('div.result-description');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img.result-image');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'darkweb') {
                            // 다크웹 검색 결과 추출 (예: Torch 기준)
                            const titleElem = item.querySelector('a');
                            const descElem = item.querySelector('p');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        }

                        // 광고 제외 기능 적용
                        if (toggleAds.checked) {
                            const combinedText = `${title} ${description}`.toLowerCase();
                            const isAd = combinedText.includes('advertisement') || combinedText.includes('광고');
                            if (isAd) {
                                log(`🚫 광고 항목 제외: "${title}"`);
                                return null;
                            }
                        }

                        // 필터 단어 적용
                        if (filterPatterns.length > 0) {
                            const combinedText = `${title} ${description}`.toLowerCase();
                            const isFiltered = filterPatterns.some(regex => regex.test(combinedText));
                            if (isFiltered) {
                                log(`❌ 필터 단어 포함 항목 제외: "${title}"`);
                                return null;
                            }
                        }

                        // 사이트 필터링 적용 (사이트 제목 또는 HTML 내용 기반)
                        if (siteFilterPatterns.length > 0) {
                            const isSiteFiltered = siteFilterPatterns.some(regex => regex.test(link) || regex.test(title));
                            if (isSiteFiltered) {
                                log(`🚫 사이트 필터링에 의해 제외된 항목: "${link}"`);
                                return null;
                            }
                        }

                        // 도메인 추출
                        const domain = extractDomain(link);
                        if (!domain) return null;

                        // 제외 도메인 검사
                        if (excludeDomains.includes(domain.toLowerCase())) {
                            log(`🚫 제외 도메인 항목 제외: "${link}"`);
                            return null;
                        }

                        // 엄격 사이트 필터링 적용
                        if (strictSiteFilter.checked) {
                            if (!siteFilterPatterns.some(regex => regex.test(link) || regex.test(title))) {
                                log(`🚫 엄격 사이트 필터링에 의해 제외된 항목: "${link}"`);
                                return null;
                            }
                        }

                        // 중복 검사 (도메인별 방문 횟수)
                        let visitCount = visitedLinks.get(domain) || 0;
                        if (visitCount >= duplicateThreshold) {
                            log(`🔄 중복 방문 제한: "${domain}"`);
                            return null;
                        }

                        // 방문 횟수 업데이트
                        visitedLinks.set(domain, visitCount + 1);
                        visitedCountElem.innerText = visitedLinks.size.toString();

                        // 페이지 로딩 시간 측정
                        const loadStart = performance.now();
                        try {
                            // mode: 'cors'로 변경하여 실제 응답을 받을 수 있도록 함
                            const fetchResponse = await fetch(link, { mode: 'cors' });
                            if (fetchResponse.ok) {
                                const loadEnd = performance.now();
                                pageLoadTime = Math.round(loadEnd - loadStart);
                            } else {
                                pageLoadTime = '응답 불량';
                            }
                        } catch (err) {
                            pageLoadTime = '계산 불가';
                        }

                        // HTML 옵션
                        if (!toggleHTML.checked) {
                            htmlLink = '비활성화';
                        }

                        // 이미지 옵션
                        if (!toggleImages.checked) {
                            image = '비활성화';
                        }

                        const result = { title, link, description, image, htmlLink, pageLoadTime };
                        results.push(result);

                        // 결과 테이블에 추가
                        const row = document.createElement('tr');
                        row.innerHTML = `
                            <td>${escapeHTML(title)}</td>
                            <td><a href="${escapeHTML(link)}" target="_blank">${escapeHTML(link)}</a></td>
                            <td>${escapeHTML(description)}</td>
                            <td>${image}</td>
                            <td>${htmlLink}</td>
                            <td>${pageLoadTime}</td>
                        `;
                        resultsBody.appendChild(row);

                        return result;
                    });

                    // Promise.all을 통해 모든 항목 처리 완료 대기
                    await Promise.all(itemPromises);

                    // 데이터 시각화 업데이트
                    updateChart();

                    // 진행률 업데이트
                    updateProgress();

                    // 대기 시간 추가하여 서버에 부담 주지 않기
                    setTimeout(() => {
                        queueCrawlPage();
                    }, isPaused ? 0 : 1000); // 1초 대기

                    resolve();
                },
                onerror: function() {
                    log(`❌ 페이지 ${page} 요청 중 오류 발생.`);
                    handleCrawlError(page, retryAttempts).then(() => {
                        resolve();
                    });
                }
            });
        });
    }

    async function handleCrawlError(page, retriesLeft) {
        if (retriesLeft > 0 && isRunning) {
            log(`🔄 페이지 ${page} 재시도 (${retriesLeft}회 남음).`);
            return new Promise((resolve) => {
                setTimeout(() => {
                    const url = constructURL(page);
                    if (!url) {
                        log(`❌ 페이지 ${page} 재시도 실패: URL이 유효하지 않습니다.`);
                        resolve();
                        return;
                    }
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: url,
                        headers: {
                            "User-Agent": navigator.userAgent,
                            "Accept": "text/html"
                        },
                        onload: async function(response) {
                            if (response.status === 200) {
                                log(`✅ 페이지 ${page} 재시도 성공.`);
                                await crawlPage(page);
                            } else {
                                await handleCrawlError(page, retriesLeft - 1);
                            }
                            resolve();
                        },
                        onerror: async function() {
                            await handleCrawlError(page, retriesLeft - 1);
                            resolve();
                        }
                    });
                }, retryInterval); // retryInterval이 100ms으로 설정됨
            });
        } else {
            log(`❌ 페이지 ${page} 로드 실패: 최대 재시도 횟수 초과.`);
            // 크롤링을 중지하지 않고 다음 페이지로 넘어감
            currentPage++;
            currentPageElem.innerText = currentPage;
            setTimeout(() => {
                queueCrawlPage();
            }, 1000);
            return Promise.resolve();
        }
    }

    function constructURL(page) {
        if (searchEngine === 'google') {
            return `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}&start=${(page-1)*10}`;
        } else if (searchEngine === 'bing') {
            return `https://www.bing.com/search?q=${encodeURIComponent(searchQuery)}&first=${(page-1)*10 +1}`;
        } else if (searchEngine === 'daum') {
            return `https://search.daum.net/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'naver') {
            return `https://search.naver.com/search.naver?query=${encodeURIComponent(searchQuery)}&start=${(page-1)*10 +1}`;
        } else if (searchEngine === 'yahoo') {
            return `https://search.yahoo.com/search?p=${encodeURIComponent(searchQuery)}&b=${(page-1)*10 +1}`;
        } else if (searchEngine === 'duckduckgo') {
            return `https://duckduckgo.com/html/?q=${encodeURIComponent(searchQuery)}&s=${(page-1)*10}`;
        } else if (searchEngine === 'ecosia') {
            return `https://www.ecosia.org/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'darkweb') {
            const darkwebEngines = getDarkwebEngines();
            if (darkwebEngines.length === 0) {
                return '';
            }
            const randomEngine = darkwebEngines[Math.floor(Math.random() * darkwebEngines.length)];
            return `${randomEngine.url}/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else {
            return '';
        }
    }

    function updateProgress() {
        // 무제한이므로 진행률을 페이지 수에 기반하여 표시
        const progressPercent = (currentPage % 100) * 1; // 100페이지마다 100%로 리셋
        progressBar.value = progressPercent > 100 ? 100 : progressPercent;
        progressText.innerText = `페이지 ${currentPage}`;
        if (progressPercent >= 100) {
            progressBar.value = 0;
        }
    }

    function resetCrawling() {
        isRunning = false;
        isPaused = false;
        currentPage = 1;
        results = [];
        visitedLinks.clear();
        resultsBody.innerHTML = '';
        progressBar.value = 0;
        progressText.innerText = '페이지 0';
        currentPageElem.innerText = '0';
        visitedCountElem.innerText = '0';
        elapsedTimeElem.innerText = '00:00:00';
        remainingTimeElem.innerText = '00:00:00';
        logMessages = [];
        logContainer.innerHTML = `<strong>로그:</strong><br>`;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
        // 차트 초기화
        crawlData.labels = [];
        crawlData.datasets[0].data = [];
        if (crawlChart) {
            crawlChart.update();
        }
        clearInterval(elapsedInterval);
        log('🔄 크롤링을 초기화하였습니다.');
    }

    function exportResults() {
        if (results.length === 0) {
            alert('내보낼 결과가 없습니다.');
            return;
        }
        const format = prompt('내보낼 형식을 선택하세요 (xlsx, csv, json):', 'xlsx');
        if (!format) return;
        if (format.toLowerCase() === 'xlsx') {
            const ws = XLSX.utils.json_to_sheet(results);
            const wb = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(wb, ws, "결과");
            XLSX.writeFile(wb, "크롤링_결과.xlsx");
            log('💾 결과를 "크롤링_결과.xlsx" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.xlsx" 파일로 내보냈습니다.',
                title: '웹 크롤러 7.9.5',
                timeout: 3000
            });
        } else if (format.toLowerCase() === 'csv') {
            const csv = results.map(row => `"${row.title.replace(/"/g, '""')}","${row.link.replace(/"/g, '""')}","${row.description.replace(/"/g, '""')}","${row.image.replace(/"/g, '""')}","${row.htmlLink.replace(/"/g, '""')}","${row.pageLoadTime}"`).join('\n');
            const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = '크롤링_결과.csv';
            a.click();
            log('💾 결과를 "크롤링_결과.csv" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.csv" 파일로 내보냈습니다.',
                title: '웹 크롤러 7.9.5',
                timeout: 3000
            });
        } else if (format.toLowerCase() === 'json') {
            const json = JSON.stringify(results, null, 2);
            const blob = new Blob([json], { type: 'application/json;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = '크롤링_결과.json';
            a.click();
            log('💾 결과를 "크롤링_결과.json" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.json" 파일로 내보냈습니다.',
                title: '웹 크롤러 7.9.5',
                timeout: 3000
            });
        } else {
            alert('지원하지 않는 형식입니다.');
        }
    }

    function escapeHTML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    function extractDomain(url) {
        try {
            const urlObj = new URL(url);
            return urlObj.hostname.replace('www.', '');
        } catch (e) {
            return null;
        }
    }

    function updateElapsedTime() {
        if (!startTime) return;
        const now = Date.now();
        const elapsed = Math.floor((now - startTime) / 1000);
        elapsedTimeElem.innerText = formatTime(elapsed);
        const estimatedTotal = currentPage * 1; // 예측: 각 페이지 1초
        const remaining = Math.max(estimatedTotal - elapsed, 0);
        remainingTimeElem.innerText = formatTime(remaining);
    }

    function formatTime(seconds) {
        const hrs = String(Math.floor(seconds / 3600)).padStart(2, '0');
        const mins = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
        const secs = String(seconds % 60).padStart(2, '0');
        return `${hrs}:${mins}:${secs}`;
    }

    function updateChart() {
        crawlData.labels = Array.from(visitedLinks.keys());
        crawlData.datasets[0].data = Array.from(visitedLinks.values());

        if (crawlChart) {
            crawlChart.update();
        }
        // 저장
        GM_setValue('crawlerData', JSON.stringify(crawlData));
    }

    function loadCrawlData() {
        const storedData = GM_getValue('crawlerData', null);
        if (storedData) {
            const parsedData = JSON.parse(storedData);
            crawlData = parsedData;
            if (crawlChart) {
                crawlChart.data = crawlData;
                crawlChart.update();
            }
        }
    }

    function saveCrawlData() {
        GM_setValue('crawlerData', JSON.stringify(crawlData));
    }

    // 주기적으로 크롤링 데이터를 저장
    setInterval(saveCrawlData, 5000);

    // 검색 엔진 바로 가기 버튼 기능
    function openSearchEngines() {
        const engineUrls = {
            'Google': 'https://www.google.com/',
            'Bing': 'https://www.bing.com/',
            'Daum': 'https://www.daum.net/',
            'Naver': 'https://www.naver.com/',
            'Yahoo': 'https://www.yahoo.com/',
            'DuckDuckGo': 'https://duckduckgo.com/',
            'Ecosia': 'https://www.ecosia.org/',
            'Yahoo Japan': 'https://www.yahoo.co.jp/',
            'Baidu': 'https://www.baidu.com/',
            'Yandex': 'https://www.yandex.com/',
            'Ask': 'https://www.ask.com/',
            'Startpage': 'https://www.startpage.com/',
            'Qwant': 'https://www.qwant.com/',
            'Searx': 'https://searx.me/',
            'Swisscows': 'https://swisscows.com/',
            'Gibiru': 'https://www.gibiru.com/',
            // 다크웹 검색 엔진도 포함
            'TOR Search': 'http://torsearch7uu5ond.onion/',
            'Ahmia': 'http://msydqstlz2kzerdg.onion/',
            'Haystak': 'http://haystakvxad7wbk5.onion/',
            'Dark Search': 'http://darksearch3w3nrc.onion/',
            'Phobos': 'http://phobos6uj4abm4pf.onion/',
            'Not Evil': 'http://hss3uro2hsxfogfq.onion/',
            'IntelligenceX': 'http://intelligencexmh5v.onion/',
            'Torch': 'http://xmh57jrzrnw6insl.onion/',
            'Silk Road 3.1': 'http://silkroad31lo5ya.onion/',
            'Fresh Onions': 'http://rx7hmtf7k7xk6ked.onion/',
            // 추가 엔진 가능
            'Dark Web Link': 'http://darkweblink5wqk.onion/',
            'Tor Tracker': 'http://tortracker5ujzra.onion/',
            'Deep Web Search': 'http://deepwebsearch6b6x.onion/',
            'Onion Monitor': 'http://onionmonitor3c7v.onion/',
            'Hidden Wiki': 'http://hiddenwiki5z35.onion/',
            'TorEX': 'http://torexk3wd2buz.onion/',
            'BlackBook': 'http://blackbook51uv.onion/',
            'Taboo Search': 'http://taboosearch37b.onion/',
            'The Pirate Bay (onion)': 'http://uj3wazyk5u4hnvtk.onion/',
            // 더 많은 검색 엔진을 추가할 수 있습니다.
        };
        let html = '📡 <strong>검색 엔진:</strong><br>';
        for (const [name, url] of Object.entries(engineUrls)) {
            html += `<button class="search-engine-btn" data-url="${url}">${name}</button><br>`;
        }
        // 팝업 생성
        const popup = document.createElement('div');
        popup.className = 'search-engines-popup';
        popup.innerHTML = html;
        container.appendChild(popup);
        popup.style.display = 'block';

        // 버튼 이벤트 리스너
        popup.querySelectorAll('.search-engine-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                window.open(btn.getAttribute('data-url'), '_blank');
                popup.style.display = 'none';
            });
        });

        // 외부 클릭 시 팝업 닫기
        document.addEventListener('click', function(event) {
            if (!popup.contains(event.target) && event.target !== searchEnginesBtn) {
                popup.style.display = 'none';
            }
        }, { once: true });
    }

    // HTML 탭에 콘텐츠 표시 기능
    container.addEventListener('click', function(event) {
        if (event.target.classList.contains('html-btn')) {
            const url = decodeURIComponent(event.target.getAttribute('data-url'));
            showHTMLInUI(url);
        }
    });

    container.addEventListener('mouseover', function(event) {
        if (event.target.classList.contains('html-btn')) {
            const url = decodeURIComponent(event.target.getAttribute('data-url'));
            showPreviewPopup(event.target, url);
        }
    });

    container.addEventListener('mouseout', function(event) {
        if (event.target.classList.contains('html-btn')) {
            const popup = container.querySelector('.preview-popup');
            if (popup) {
                popup.style.display = 'none';
            }
        }
    });

    function showHTMLInUI(url) {
        // UI 내에 iframe을 사용하여 사이트 내용 표시
        let iframe = container.querySelector('.html-display iframe');
        if (!iframe) {
            iframe = document.createElement('iframe');
            container.querySelector('.html-display').appendChild(iframe);
        }
        iframe.src = url;
        // 탭을 'HTML'로 전환
        switchTab('html-display');
        log(`🔍 HTML 보기: ${url}`);
    }

    function showPreviewPopup(target, url) {
        // 팝업 생성
        let popup = container.querySelector('.preview-popup');
        if (!popup) {
            popup = document.createElement('div');
            popup.className = 'preview-popup';
            popup.innerHTML = `<iframe src="${url}" style="width:100%; height:100%; border:none;"></iframe>`;
            container.appendChild(popup);
        }
        popup.style.display = 'block';
        // 위치 조정
        const rect = target.getBoundingClientRect();
        const containerRect = container.getBoundingClientRect();
        popup.style.left = `${rect.left - containerRect.left + 20}px`;
        popup.style.top = `${rect.bottom - containerRect.top + 5}px`;
    }

    function switchTab(tabId) {
        const tabs = container.querySelectorAll('.tab');
        const tabContents = container.querySelectorAll('.tab-content');

        tabs.forEach(t => {
            if(t.getAttribute('data-tab') === tabId){
                t.classList.add('active');
            } else {
                t.classList.remove('active');
            }
        });

        tabContents.forEach(content => {
            if(content.id === tabId){
                content.classList.add('active');
            } else {
                content.classList.remove('active');
            }
        });
    }

    // 다크웹 검색 엔진 관리
    function loadDarkwebEngines() {
        const storedEngines = GM_getValue('darkwebEngines', defaultDarkwebEngines);
        darkwebEnginesList.innerHTML = '';
        storedEngines.forEach(engine => {
            const li = document.createElement('li');
            li.innerHTML = `
                <span>${escapeHTML(engine.name)}</span>
                <button class="remove-darkweb-engine-btn" data-url="${engine.url}">🗑️</button>
            `;
            darkwebEnginesList.appendChild(li);
        });

        // 삭제 버튼 이벤트 리스너
        darkwebEnginesList.querySelectorAll('.remove-darkweb-engine-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                const url = btn.getAttribute('data-url');
                removeDarkwebEngine(url);
            });
        });
    }

    function addDarkwebEngine() {
        const name = prompt('다크웹 검색 엔진 이름을 입력하세요:');
        if (!name) {
            alert('이름을 입력하지 않았습니다.');
            return;
        }
        const url = prompt('다크웹 검색 엔진 URL을 입력하세요 (예: http://exampleonion.onion/):');
        if (!url) {
            alert('URL을 입력하지 않았습니다.');
            return;
        }
        const newEngine = { name, url };
        const storedEngines = GM_getValue('darkwebEngines', defaultDarkwebEngines);
        storedEngines.push(newEngine);
        GM_setValue('darkwebEngines', storedEngines);
        loadDarkwebEngines();
        log(`🔄 다크웹 검색 엔진 추가: ${name} (${url})`);
    }

    function removeDarkwebEngine(url) {
        let storedEngines = GM_getValue('darkwebEngines', defaultDarkwebEngines);
        storedEngines = storedEngines.filter(engine => engine.url !== url);
        GM_setValue('darkwebEngines', storedEngines);
        loadDarkwebEngines();
        log(`🔄 다크웹 검색 엔진 제거: ${url}`);
    }

    function getDarkwebEngines() {
        return GM_getValue('darkwebEngines', defaultDarkwebEngines);
    }

    // 번역기 기능 추가
    async function translateContent() {
        if (results.length === 0) {
            alert('번역할 결과가 없습니다.');
            return;
        }
        translatedContent.innerHTML = '번역 중...';

        try {
            // i18next를 이용한 번역
            const texts = results.map(r => r.description).join('. ');
            const translatedText = await translateText(texts, translateLanguage);
            translatedContent.innerText = translatedText;
            log('🔄 번역이 완료되었습니다.');
        } catch (error) {
            translatedContent.innerText = '번역 중 오류가 발생했습니다.';
            log(`❌ 번역 오류: ${error}`);
        }
    }

    async function translateText(text, targetLang) {
        // Papago 번역 API 사용 (API 키 필요)
        const apiUrl = 'https://openapi.naver.com/v1/papago/n2mt';
        const clientId = 'YOUR_NAVER_CLIENT_ID'; // [필수] 실제 클라이언트 ID로 교체하세요.
        const clientSecret = 'YOUR_NAVER_CLIENT_SECRET'; // [필수] 실제 클라이언트 시크릿으로 교체하세요.

        const data = new URLSearchParams();
        data.append('source', 'ko');
        data.append('target', targetLang);
        data.append('text', text);

        const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'X-Naver-Client-Id': clientId,
                'X-Naver-Client-Secret': clientSecret
            },
            body: data
        });

        const result = await response.json();
        if (result.errorMessage) {
            throw new Error(result.errorMessage);
        }
        return result.message.result.translatedText;
    }

    // 번역기 섹션 버튼 클릭 처리
    // 번역 기능은 위에서 정의되었습니다.

    // 로깅, 필터링, 크롤링 함수들은 이미 위에서 상세히 구현되었습니다.

    // 검색 엔진 바로가기 직접 UI에 표시
    function loadSearchEnginesDirect() {
        const engineUrls = {
            'Google': 'https://www.google.com/',
            'Bing': 'https://www.bing.com/',
            'Daum': 'https://www.daum.net/',
            'Naver': 'https://www.naver.com/',
            'Yahoo': 'https://www.yahoo.com/',
            'DuckDuckGo': 'https://duckduckgo.com/',
            'Ecosia': 'https://www.ecosia.org/',
            'Yahoo Japan': 'https://www.yahoo.co.jp/',
            'Baidu': 'https://www.baidu.com/',
            'Yandex': 'https://www.yandex.com/',
            'Ask': 'https://www.ask.com/',
            'Startpage': 'https://www.startpage.com/',
            'Qwant': 'https://www.qwant.com/',
            'Searx': 'https://searx.me/',
            'Swisscows': 'https://swisscows.com/',
            'Gibiru': 'https://www.gibiru.com/',
            'DuckDuckGo (Privacy)': 'https://duckduckgo.com/?t=hk&kat=privacy',
            'Brave Search': 'https://search.brave.com/',
            'Kiddle': 'https://www.kiddle.co/',
            'Gigablast': 'https://www.gigablast.com/',
            'Mojeek': 'https://www.mojeek.com/',
            'Yippy': 'https://yippy.com/',
            'Infinity Search': 'https://www.infinitysearch.co/',
            'WolframAlpha': 'https://www.wolframalpha.com/',
            'Shodan': 'https://www.shodan.io/',
            // 다크웹 검색 엔진도 포함
            'TOR Search': 'http://torsearch7uu5ond.onion/',
            'Ahmia': 'http://msydqstlz2kzerdg.onion/',
            'Haystak': 'http://haystakvxad7wbk5.onion/',
            'Dark Search': 'http://darksearch3w3nrc.onion/',
            'Phobos': 'http://phobos6uj4abm4pf.onion/',
            'Not Evil': 'http://hss3uro2hsxfogfq.onion/',
            'IntelligenceX': 'http://intelligencexmh5v.onion/',
            'Torch': 'http://xmh57jrzrnw6insl.onion/',
            'Silk Road 3.1': 'http://silkroad31lo5ya.onion/',
            'Fresh Onions': 'http://rx7hmtf7k7xk6ked.onion/',
            'Dark Web Link': 'http://darkweblink5wqk.onion/',
            'Tor Tracker': 'http://tortracker5ujzra.onion/',
            'Deep Web Search': 'http://deepwebsearch6b6x.onion/',
            'Onion Monitor': 'http://onionmonitor3c7v.onion/',
            'Hidden Wiki': 'http://hiddenwiki5z35.onion/',
            'TorEX': 'http://torexk3wd2buz.onion/',
            'BlackBook': 'http://blackbook51uv.onion/',
            'Taboo Search': 'http://taboosearch37b.onion/',
            'The Pirate Bay (onion)': 'http://uj3wazyk5u4hnvtk.onion/',
            // 더 많은 검색 엔진을 추가할 수 있습니다.
        };

        let html = '';
        for (const [name, url] of Object.entries(engineUrls)) {
            html += `<button class="search-engine-direct-btn" data-url="${url}">${name}</button>`;
        }

        searchEnginesDirect.innerHTML = html;

        // 버튼 이벤트 리스너
        searchEnginesDirect.querySelectorAll('.search-engine-direct-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                window.open(btn.getAttribute('data-url'), '_blank');
            });
        });
    }

})();                                                                        Google
URL: https://www.google.com/
Bing
URL: https://www.bing.com/
Yahoo
URL: https://www.yahoo.com/
DuckDuckGo
URL: https://duckduckgo.com/
Ecosia
URL: https://www.ecosia.org/
Ask.com
URL: https://www.ask.com/
AOL Search
URL: https://search.aol.com/
Baidu
URL: https://www.baidu.com/
Yandex
URL: https://www.yandex.com/
Startpage
URL: https://www.startpage.com/
🔒 프라이버시 중심 검색 엔진
Qwant
URL: https://www.qwant.com/
Swisscows
URL: https://swisscows.com/
Mojeek
URL: https://www.mojeek.com/
Gibiru
URL: https://www.gibiru.com/
MetaGer
URL: https://metager.org/
Peekier
URL: https://peekier.com/
Disconnect Search
URL: https://search.disconnect.me/
Ekoru
URL: https://www.ekoru.org/
Yippy
URL: https://yippy.com/
Infinity Search
URL: https://www.infinitysearch.co/
🌍 지역별 검색 엔진
Naver (한국)
URL: https://www.naver.com/
Daum (한국)
URL: https://www.daum.net/
Sogou (중국)
URL: https://www.sogou.com/
Nate (한국)
URL: https://www.nate.com/
Seznam (체코)
URL: https://www.seznam.cz/
Yandex.ru (러시아)
URL: https://www.yandex.ru/
Qwant.fr (프랑스)
URL: https://www.qwant.com/
Zyxc (독일)
URL: https://www.zyxc.search.de/
Exalead (프랑스)
URL: https://www.exalead.com/search/
Biglobe (일본)
URL: https://www.biglobe.ne.jp/
🛠️ 전문 검색 엔진
WolframAlpha (지식 엔진)
URL: https://www.wolframalpha.com/
Shodan (사물인터넷 검색)
URL: https://www.shodan.io/
Archive.org (인터넷 아카이브)
URL: https://archive.org/
Wayback Machine (웹사이트 아카이브)
URL: https://web.archive.org/
PubMed (의학 논문)
URL: https://pubmed.ncbi.nlm.nih.gov/
IEEE Xplore (공학 논문)
URL: https://ieeexplore.ieee.org/
Google Scholar (학술 자료)
URL: https://scholar.google.com/
LexisNexis (법률 자료)
URL: https://www.lexisnexis.com/
Google Books (도서 검색)
URL: https://books.google.com/
Creative Commons Search (저작권 무료 자료)
URL: https://search.creativecommons.org/
🌐 다크웹 검색 엔진
Torch
URL: http://xmh57jrzrnw6insl.onion/
Ahmia
URL: http://msydqstlz2kzerdg.onion/
Haystak
URL: http://haystakvxad7wbk5.onion/
Not Evil
URL: http://hss3uro2hsxfogfq.onion/
Phobos
URL: http://phobos6uj4abm4pf.onion/
DarkSearch
URL: http://darksearch3w3nrc.onion/
IntelligenceX
URL: http://intelligencexmh5v.onion/
The Pirate Bay (onion)
URL: http://uj3wazyk5u4hnvtk.onion/
Silk Road 3.1
URL: http://silkroad31lo5ya.onion/
Deep Web Search
URL: http://deepwebsearch6b6x.onion/
🔍 특화된 기능의 검색 엔진
Dogpile (메타 검색 엔진)
URL: https://www.dogpile.com/
Startpage (프라이버시 중심)
URL: https://www.startpage.com/
Exalead (비즈니스 중심)
URL: https://www.exalead.com/
Gigablast (오픈소스 검색 엔진)
URL: https://www.gigablast.com/
Searx (오픈소스 메타 검색 엔진)
URL: https://searx.me/
FindSounds (음악 검색)
URL: https://www.findsounds.com/
TinEye (이미지 역검색)
URL: https://tineye.com/
Boardreader (포럼 검색)
URL: https://www.boardreader.com/
PubCrawl (학술 논문 연관 검색)
URL: https://pubcrawl.org/
BeautyFetch (사진 및 이미지 검색)
URL: http://beautyfetch.com/
🌟 추가 가능한 기타 검색 엔진
Search Encrypt
URL: https://www.searchencrypt.com/
Oscobo
URL: https://www.oscobo.com/
HotBot
URL: https://www.hotbot.com/
Peekier
URL: https://peekier.com/
Lukol
URL: https://www.lukol.com/
Disconnect Search
URL: https://search.disconnect.me/
Privatelee
URL: https://privatelee.com/
Infinity Search
URL: https://www.infinitysearch.co/
DuckDuckGo (Tor)
URL: http://3g2upl4pq6kufc4m.onion/
TorEX
URL: http://torexk3wd2buz.onion/
BlackBook
URL: http://blackbook51uv.onion/
Taboo Search
URL: http://taboosearch37b.onion/
Kilos
URL: http://kilos7wbfn3ptgw.onion/
Candle
URL: http://zg6ak7qqfhbkbppx.onion/
Dark Falcon
URL: http://darkfalconphp5fh.onion/
Hidden Wiki
URL: http://hiddenwiki5z35.onion/
Onion Monitor
URL: http://onionmonitor3c7v.onion/
Phobos 2
URL: http://phobos2uj4abm4pf.onion/
Deep Web Search
URL: http://deepwebsearch6b6x.onion/
Dark Web Link
URL: http://darkweblink5wqk.onion/
Tor Tracker
URL: http://tortracker5ujzra.onion/
IntelligenceX
URL: http://intelligencexmh5v.onion/
Fresh Onions
URL: http://rx7hmtf7k7xk6ked.onion/
Phobos
URL: http://phobos6uj4abm4pf.onion/
Not Evil
URL: http://hss3uro2hsxfogfq.onion/
The Pirate Bay (onion)
URL: http://uj3wazyk5u4hnvtk.onion/
Shinobi Search
URL: http://shinobisearch6airie.onion/
Royal Dark Web Search
URL: http://royaldarkwebsearch57af.onion/
DarkSearchBox
URL: http://darksearchbox5a3x.onion/
Black Search
URL: http://blacksearch7xm7.onion/
📚 기타 유용한 검색 엔진
Google Images
URL: https://images.google.com/
Bing Images
URL: https://www.bing.com/images
DuckDuckGo Images
URL: https://duckduckgo.com/?ia=images
Pinterest
URL: https://www.pinterest.com/
Flickr
URL: https://www.flickr.com/
Shutterstock
URL: https://www.shutterstock.com/
Unsplash
URL: https://unsplash.com/
Pixabay
URL: https://pixabay.com/
500px
URL: https://500px.com/
Goodreads (도서 검색)
URL: https://www.goodreads.com/  이것들을 검색 엔진 탭에 추가하셈  + 코드만 쓰셈 코드 생략하지 마셈 코드 삭제 하지 마셈 
ASSISTANT
```javascript
// ==UserScript==
// @name         종합 웹 크롤러 7.9.5
// @namespace    http://tampermonkey.net/
// @version      7.9.5
// @description  고급 기능을 갖춘 웹 크롤러로 검색 결과를 실시간으로 수집하고 표시 (동시 크롤링, 번역 기능, UI 개선 등)
// @author       YourName
// @match        *://www.google.com/search*
// @match        *://www.bing.com/search*
// @match        *://search.daum.net/search*
// @match        *://search.naver.com/search*
// @match        *://search.yahoo.com/search*
// @match        *://duckduckgo.com/*
// @match        *://www.ecosia.org/search*
// @match        *://*.onion/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_notification
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @require      https://cdn.jsdelivr.net/npm/chart.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/i18next/21.6.15/i18next.min.js
// ==/UserScript==

(function() {
    'use strict';

    // CSS 스타일 추가 (UI 대폭 개선)
    GM_addStyle(`
        /* 컨테이너 스타일 */
        #crawler-container {
            position: fixed;
            top: 20px;
            right: 20px;
            background-color: #2c3e50;
            border: 2px solid #1abc9c;
            padding: 20px;
            z-index: 10000;
            width: 800px;
            max-height: 85vh;
            overflow: hidden;
            box-shadow: 0 8px 30px rgba(0,0,0,0.5);
            border-radius: 12px;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            color: #ecf0f1;
            display: flex;
            flex-direction: column;
            transition: all 0.3s ease;
        }

        /* 헤더 스타일 */
        #crawler-container header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }

        #crawler-container .title {
            font-size: 20px;
            font-weight: bold;
        }

        #crawler-container .close-btn {
            cursor: pointer;
            font-size: 24px;
            color: #e74c3c;
            transition: color 0.3s;
        }

        #crawler-container .close-btn:hover {
            color: #c0392b;
        }

        /* 탭 스타일 */
        #crawler-container .tabs {
            display: flex;
            border-bottom: 2px solid #1abc9c;
            margin-bottom: 15px;
            overflow-x: auto;
        }

        #crawler-container .tab {
            flex: 1;
            text-align: center;
            padding: 10px 0;
            cursor: pointer;
            background-color: #34495e;
            border-right: 1px solid #1abc9c;
            transition: background-color 0.3s, color 0.3s;
            white-space: nowrap;
        }

        #crawler-container .tab:last-child {
            border-right: none;
        }

        #crawler-container .tab.active {
            background-color: #1abc9c;
            color: #2c3e50;
            font-weight: bold;
        }

        /* 탭 콘텐츠 스타일 */
        #crawler-container .tab-content {
            display: none;
            flex: 1;
            overflow-y: auto;
        }

        #crawler-container .tab-content.active {
            display: block;
        }

        /* 버튼 스타일 */
        #crawler-container .controls button {
            background-color: #1abc9c;
            border: none;
            color: #2c3e50;
            padding: 10px 16px;
            margin: 6px 3px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s, transform 0.2s;
            font-size: 14px;
            display: flex;
            align-items: center;
        }

        #crawler-container .controls button:hover {
            background-color: #16a085;
            transform: translateY(-2px);
        }

        #crawler-container .controls button i {
            margin-right: 6px;
        }

        /* 체크박스 및 라벨 스타일 */
        #crawler-container .options label,
        #crawler-container .filters label,
        #crawler-container .exclude-domains label,
        #crawler-container .advanced-options label {
            display: flex;
            align-items: center;
            margin: 10px 0;
            font-size: 14px;
        }

        #crawler-container .options input[type="checkbox"] {
            margin-right: 8px;
            transform: scale(1.2);
        }

        /* 스위치 스타일 */
        .switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
            margin-left: 10px;
        }

        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .switch .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: 0.4s;
            border-radius: 24px;
        }

        .switch .slider:before {
            position: absolute;
            content: "";
            height: 18px;
            width: 18px;
            left: 3px;
            bottom: 3px;
            background-color: white;
            transition: 0.4s;
            border-radius: 50%;
        }

        .switch input:checked + .slider {
            background-color: #1abc9c;
        }

        .switch input:checked + .slider:before {
            transform: translateX(26px);
        }

        /* 입력 필드 스타일 */
        #crawler-container input[type="text"],
        #crawler-container input[type="number"] {
            width: 100%;
            padding: 8px;
            margin: 6px 0;
            border: 1px solid #bdc3c7;
            border-radius: 4px;
            font-size: 14px;
        }

        /* 테이블 스타일 */
        #resultsTable {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }

        #resultsTable th, #resultsTable td {
            border: 1px solid #95a5a6;
            padding: 10px;
            text-align: left;
            font-size: 13px;
        }

        #resultsTable th {
            background-color: #1abc9c;
            color: #2c3e50;
        }

        #resultsTable tbody tr:nth-child(even) {
            background-color: #34495e;
        }

        /* 반응형 디자인 */
        @media (max-width: 900px) {
            #crawler-container {
                width: 90%;
                right: 5%;
                padding: 15px;
            }

            #crawler-container .controls button {
                padding: 8px 12px;
                font-size: 13px;
            }

            #crawler-container .title {
                font-size: 18px;
            }
        }

        /* Papago iframe 스타일 */
        #papagoIframe {
            width: 100%;
            height: 100%;
            border: none;
        }

        /* 팝업 스타일 */
        .search-engines-popup, .manage-darkweb-popup {
            position: absolute;
            background-color: #2c3e50;
            border: 1px solid #1abc9c;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            max-height: 350px;
            overflow-y: auto;
            z-index: 10001;
        }

        .search-engines-popup button, .manage-darkweb-popup button {
            width: 100%;
            text-align: left;
            margin: 6px 0;
            background-color: #34495e;
            color: #ecf0f1;
            border: none;
            padding: 10px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 14px;
        }

        .search-engines-popup button:hover, .manage-darkweb-popup button:hover {
            background-color: #1abc9c;
            color: #2c3e50;
        }

        /* 로그 스타일 */
        #log {
            background-color: #34495e;
            padding: 10px;
            border-radius: 5px;
            max-height: 200px;
            overflow-y: auto;
            font-size: 13px;
            line-height: 1.4;
        }

        /* 차트 컨테이너 */
        .chart-container {
            margin-top: 15px;
            height: 250px;
        }

        /* HTML 디스플레이 스타일 */
        .html-display iframe {
            width: 100%;
            height: 500px;
            border: none;
            border-radius: 5px;
        }

        /* Papago 디스플레이 스타일 */
        .papago-display {
            width: 100%;
            height: 500px;
            position: relative;
        }

        /* 다크웹 엔진 목록 스타일 */
        #darkwebEnginesList li {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
        }

        #darkwebEnginesList li span {
            flex: 1;
            margin-right: 10px;
            word-break: break-all;
        }

        #darkwebEnginesList li button {
            background-color: #e74c3c;
            border: none;
            color: #fff;
            padding: 6px 10px;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        #darkwebEnginesList li button:hover {
            background-color: #c0392b;
        }

        /* 예외 도메인 리스트 스타일 */
        .exclude-domains ul {
            list-style: none;
            padding: 0;
            margin-top: 10px;
        }

        .exclude-domains ul li {
            background-color: #34495e;
            padding: 6px 10px;
            border-radius: 4px;
            margin-bottom: 6px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .exclude-domains ul li button {
            background-color: #e74c3c;
            border: none;
            color: #fff;
            padding: 4px 8px;
            border-radius: 3px;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 12px;
        }

        .exclude-domains ul li button:hover {
            background-color: #c0392b;
        }

        /* Search Engines Direct UI */
        #search-engines-direct {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 15px;
        }

        #search-engines-direct button {
            flex: 1 1 45%;
            background-color: #1abc9c;
            border: none;
            color: #2c3e50;
            padding: 8px 12px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 14px;
        }

        #search-engines-direct button:hover {
            background-color: #16a085;
        }

        /* Preview Popup */
        .preview-popup {
            position: absolute;
            background-color: #2c3e50;
            border: 1px solid #1abc9c;
            padding: 10px;
            border-radius: 6px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            width: 300px;
            height: 150px;
            z-index: 10002;
            display: none;
        }
    `);

    // UI 생성 (대폭 개선된 디자인)
    const container = document.createElement('div');
    container.id = 'crawler-container';
    container.innerHTML = `
        <header>
            <span class="title">🔥 웹 크롤러 7.9.5</span>
            <span class="close-btn" title="닫기">&times;</span>
        </header>
        <div class="tabs">
            <div class="tab active" data-tab="settings">설정</div>
            <div class="tab" data-tab="results">결과</div>
            <div class="tab" data-tab="logs">로그</div>
            <div class="tab" data-tab="site-filters">사이트 필터</div>
            <div class="tab" data-tab="darkweb">다크웹</div>
            <div class="tab" data-tab="html-display">HTML</div>
            <div class="tab" data-tab="papago">Papago</div>
            <div class="tab" data-tab="search-engines">검색 엔진</div>
        </div>
        <div class="tab-content active" id="settings">
            <!-- 제어 버튼 -->
            <div class="controls">
                <button id="startBtn" title="크롤링 시작"><i>▶️</i> 시작</button>
                <button id="pauseBtn" class="pause-btn" title="크롤링 일시정지/재개"><i>⏸️</i> 일시정지</button>
                <button id="stopBtn" title="크롤링 중지"><i>⏹️</i> 중지</button>
                <button id="resetBtn" title="크롤링 초기화"><i>🔄</i> 초기화</button>
                <button id="exportBtn" class="export-btn" title="결과 내보내기"><i>💾</i> 내보내기</button>
                <button id="searchEnginesBtn" class="search-engines-btn" title="검색 엔진 바로가기"><i>🔍</i> 검색 엔진</button>
            </div>
            <!-- 옵션 섹션 -->
            <div class="options">
                <label><input type="checkbox" id="toggleHTML" checked> HTML 가져오기</label>
                <label><input type="checkbox" id="toggleImages" checked> 이미지 가져오기</label>
                <label><input type="checkbox" id="toggleAds" checked> 광고 제외</label>
                <label>엄격 사이트 필터링
                    <label class="switch">
                        <input type="checkbox" id="strictSiteFilter">
                        <span class="slider"></span>
                    </label>
                </label>
                <label>필터링 기능 활성화
                    <label class="switch">
                        <input type="checkbox" id="filteringToggle" checked>
                        <span class="slider"></span>
                    </label>
                </label>
            </div>
            <!-- 필터 섹션 -->
            <div class="filters">
                <label for="filterInput">필터 단어 (정규식, 쉼표로 구분):</label>
                <input type="text" id="filterInput" placeholder="예: 기부, 광고, /promo/i">
            </div>
            <!-- 도메인 제외 섹션 -->
            <div class="exclude-domains">
                <label for="excludeDomainsInput">제외할 도메인 (쉼표로 구분):</label>
                <input type="text" id="excludeDomainsInput" placeholder="예: facebook.com, youtube.com">
                <ul id="excludeDomainsList"></ul>
            </div>
            <!-- 고급 옵션 섹션 -->
            <div class="advanced-options">
                <label for="duplicateThreshold">중복 허용 횟수:</label>
                <input type="number" id="duplicateThreshold" value="5" min="1" style="width: 80px;">
            </div>
            <!-- 제어 옵션 섹션 -->
            <div class="controls">
                <label for="retryAttempts">재시도 횟수:</label>
                <input type="number" id="retryAttempts" value="10" min="1" style="width: 80px;">
                <label for="retryInterval">재시도 간격 (ms):</label>
                <input type="number" id="retryInterval" value="100" min="100" style="width: 80px;">
            </div>
            <!-- 다크웹 검색 엔진 토글 -->
            <div class="controls">
                <label>다크웹 검색 엔진 사용
                    <label class="switch">
                        <input type="checkbox" id="darkwebToggle" checked>
                        <span class="slider"></span>
                    </label>
                </label>
            </div>
            <!-- 진행률 표시 섹션 -->
            <div class="progress-section">
                <progress id="progressBar" value="0" max="100" style="width: 100%; height: 20px;"></progress>
                <span id="progressText">페이지 0</span>
            </div>
            <!-- 상태 정보 섹션 -->
            <div class="status">
                현재 페이지: <span id="currentPage">0</span>
                <br>
                방문된 사이트: <span id="visitedCount">0</span>
                <br>
                경과 시간: <span id="elapsedTime">00:00:00</span>
                <br>
                예상 남은 시간: <span id="remainingTime">00:00:00</span>
            </div>
            <!-- 차트 섹션 -->
            <div class="chart-container">
                <canvas id="crawlChart"></canvas>
            </div>
        </div>
        <div class="tab-content" id="results">
            <div style="margin-top:15px; overflow: auto;">
                <table id="resultsTable">
                    <thead>
                        <tr>
                            <th>사이트 이름</th>
                            <th>주소</th>
                            <th>설명</th>
                            <th>이미지</th>
                            <th>HTML 링크</th>
                            <th>페이지 로딩 시간 (ms)</th>
                        </tr>
                    </thead>
                    <tbody id="resultsBody">
                    </tbody>
                </table>
            </div>
        </div>
        <div class="tab-content" id="logs">
            <div class="log" id="log">
                <strong>로그:</strong><br>
            </div>
        </div>
        <div class="tab-content" id="site-filters">
            <div class="site-filters">
                <label for="siteFilterInput">사이트 필터링 (정규식, 쉼표로 구분):</label>
                <input type="text" id="siteFilterInput" placeholder="예: .*example\\.com.*, /^https?:\\/\\/.*\\.example\\.org$/">
            </div>
        </div>
        <div class="tab-content" id="darkweb">
            <div class="darkweb-search">
                <label>다크웹 검색 엔진 추가/관리</label>
                <button class="add-darkweb-engine-btn" title="다크웹 검색 엔진 추가"><i>➕</i> 추가</button>
                <ul id="darkwebEnginesList">
                    <!-- 다크웹 검색 엔진 목록 -->
                </ul>
            </div>
        </div>
        <div class="tab-content" id="html-display">
            <div class="html-display"></div>
        </div>
        <div class="tab-content" id="papago">
            <div class="papago-display">
                <iframe id="papagoIframe" src="https://papago.naver.com/"></iframe>
            </div>
        </div>
        <div class="tab-content" id="search-engines">
            <div class="search-engines-direct" id="search-engines-direct">
                <!-- 검색 엔진 바로가기 버튼 추가 -->
            </div>
        </div>
    `;
    document.body.appendChild(container);

    // 드래그 가능하게 설정
    (function makeDraggable(elmnt) {
        const header = elmnt.querySelector('header');
        const closeBtn = elmnt.querySelector('.close-btn');
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = dragMouseDown;

        closeBtn.onclick = () => {
            elmnt.style.display = 'none';
        }

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
            elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    })(container);

    // 탭 기능 구현
    (function setupTabs() {
        const tabs = container.querySelectorAll('.tab');
        const tabContents = container.querySelectorAll('.tab-content');

        tabs.forEach(tab => {
            tab.addEventListener('click', () => {
                tabs.forEach(t => t.classList.remove('active'));
                tab.classList.add('active');

                const target = tab.getAttribute('data-tab');
                tabContents.forEach(content => {
                    if(content.id === target){
                        content.classList.add('active');
                    } else {
                        content.classList.remove('active');
                    }
                });
            });
        });
    })();

    // 변수 초기화
    let isRunning = false;
    let isPaused = false;
    let currentPage = 1;
    let results = [];
    let searchQuery = '';
    let searchEngine = '';
    let visitedLinks = new Map(); // 도메인별 방문 횟수 추적
    let logMessages = [];
    let filterPatterns = [];
    let excludeDomains = [];
    let siteFilterPatterns = [];
    let duplicateThreshold = 5; // 중복 허용 횟수
    let retryAttempts = 10; // 재시도 횟수
    let retryInterval = 100; // 재시도 간격 100ms으로 설정
    let startTime = null;
    let elapsedInterval = null;
    let crawlChart = null;
    let crawlData = {
        labels: [],  // 도메인 이름
        datasets: [{
            label: '방문 횟수',
            data: [],
            backgroundColor: 'rgba(46, 204, 113, 0.6)',
            borderColor: 'rgba(46, 204, 113, 1)',
            borderWidth: 1
        }]
    };
    let filteringEnabled = true; // 필터링 활성화 여부
    let concurrentRequests = 40; // 동시 요청 수 40으로 설정
    let currentRequests = 0; // 현재 요청 수
    let requestQueue = []; // 요청 대기열
    let translateLanguage = 'en'; // 기본 번역 언어
    let darkwebEnabled = true; // 다크웹 검색 엔진 사용 여부

    // 다크웹 검색 엔진 목록 (기본 엔진 추가 및 확장)
    const defaultDarkwebEngines = [
        { name: 'Torch', url: 'http://xmh57jrzrnw6insl.onion/' },
        { name: 'Ahmia', url: 'http://msydqstlz2kzerdg.onion/' },
        { name: 'Haystak', url: 'http://haystakvxad7wbk5.onion/' },
        { name: 'Not Evil', url: 'http://hss3uro2hsxfogfq.onion/' },
        { name: 'Phobos', url: 'http://phobos6uj4abm4pf.onion/' },
        { name: 'DarkSearch', url: 'http://darksearch3w3nrc.onion/' },
        { name: 'IntelligenceX', url: 'http://intelligencexmh5v.onion/' },
        { name: 'Silk Road 3.1', url: 'http://silkroad31lo5ya.onion/' },
        { name: 'Fresh Onions', url: 'http://rx7hmtf7k7xk6ked.onion/' },
        { name: 'Dark Web Link', url: 'http://darkweblink5wqk.onion/' },
        { name: 'Tor Tracker', url: 'http://tortracker5ujzra.onion/' },
        { name: 'Deep Web Search', url: 'http://deepwebsearch6b6x.onion/' },
        { name: 'Onion Monitor', url: 'http://onionmonitor3c7v.onion/' },
        { name: 'Hidden Wiki', url: 'http://hiddenwiki5z35.onion/' },
        { name: 'TorEX', url: 'http://torexk3wd2buz.onion/' },
        { name: 'BlackBook', url: 'http://blackbook51uv.onion/' },
        { name: 'Taboo Search', url: 'http://taboosearch37b.onion/' },
        { name: 'The Pirate Bay (onion)', url: 'http://uj3wazyk5u4hnvtk.onion/' },
        // 더 많은 다크웹 검색 엔진을 추가할 수 있습니다.
    ];

    // 버튼 및 요소 가져오기
    const startBtn = document.getElementById('startBtn');
    const pauseBtn = document.getElementById('pauseBtn');
    const stopBtn = document.getElementById('stopBtn');
    const resetBtn = document.getElementById('resetBtn');
    const exportBtn = document.getElementById('exportBtn');
    const searchEnginesBtn = document.getElementById('searchEnginesBtn');
    const progressBar = document.getElementById('progressBar');
    const progressText = document.getElementById('progressText');
    const resultsBody = document.getElementById('resultsBody');
    const logContainer = document.getElementById('log');
    const currentPageElem = document.getElementById('currentPage');
    const toggleHTML = document.getElementById('toggleHTML');
    const toggleImages = document.getElementById('toggleImages');
    const toggleAds = document.getElementById('toggleAds');
    const filterInput = document.getElementById('filterInput');
    const excludeDomainsInput = document.getElementById('excludeDomainsInput');
    const excludeDomainsList = document.getElementById('excludeDomainsList');
    const siteFilterInput = document.getElementById('siteFilterInput');
    const duplicateThresholdInput = document.getElementById('duplicateThreshold');
    const retryAttemptsInput = document.getElementById('retryAttempts');
    const retryIntervalInput = document.getElementById('retryInterval');
    const strictSiteFilter = document.getElementById('strictSiteFilter');
    const visitedCountElem = document.getElementById('visitedCount');
    const elapsedTimeElem = document.getElementById('elapsedTime');
    const remainingTimeElem = document.getElementById('remainingTime');
    const crawlChartCtx = document.getElementById('crawlChart').getContext('2d');
    const filteringToggle = document.getElementById('filteringToggle');
    const darkwebEnginesList = document.getElementById('darkwebEnginesList');
    const addDarkwebEngineBtn = container.querySelector('.add-darkweb-engine-btn');
    const darkwebToggle = document.getElementById('darkwebToggle');
    const papagoIframe = document.getElementById('papagoIframe');
    const searchEnginesDirect = document.getElementById('search-engines-direct');

    // 이벤트 리스너
    startBtn.addEventListener('click', startCrawling);
    pauseBtn.addEventListener('click', togglePause);
    stopBtn.addEventListener('click', stopCrawling);
    resetBtn.addEventListener('click', resetCrawling);
    exportBtn.addEventListener('click', exportResults);
    searchEnginesBtn.addEventListener('click', openSearchEngines);
    filterInput.addEventListener('input', updateFilterPatterns);
    excludeDomainsInput.addEventListener('keydown', function(e) {
        if (e.key === 'Enter') {
            addExcludeDomain();
        }
    });
    addDarkwebEngineBtn.addEventListener('click', addDarkwebEngine);
    siteFilterInput.addEventListener('input', updateSiteFilterPatterns);
    duplicateThresholdInput.addEventListener('change', updateDuplicateThreshold);
    retryAttemptsInput.addEventListener('change', updateRetryAttempts);
    retryIntervalInput.addEventListener('change', updateRetryInterval);
    strictSiteFilter.addEventListener('change', updateStrictSiteFilter);
    filteringToggle.addEventListener('change', toggleFiltering);
    darkwebToggle.addEventListener('change', toggleDarkweb);
    papagoIframe.addEventListener('load', () => {
        log('🌐 Papago 번역기 로드 완료.');
    });

    // 페이지 새로 고침 시 크롤링 초기화
    window.addEventListener('beforeunload', () => {
        resetCrawling();
    });

    // 초기화 로드
    loadLogs();
    loadCrawlData();
    loadDarkwebEngines();
    loadExcludeDomains();
    loadSearchEnginesDirect();

    // 차트 초기화
    function initializeChart() {
        crawlChart = new Chart(crawlChartCtx, {
            type: 'bar',
            data: crawlData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: { beginAtZero: true },
                    x: { ticks: { autoSkip: false } }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });
    }
    initializeChart();

    // 로깅 함수
    function log(message) {
        const time = new Date().toLocaleTimeString();
        const entry = `[${time}] ${message}`;
        logMessages.push(entry);
        logContainer.innerHTML += `${entry}<br>`;
        logContainer.scrollTop = logContainer.scrollHeight;
        // 저장
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
    }

    function loadLogs() {
        const storedLogs = GM_getValue('crawlerLogs', []);
        if (storedLogs.length > 0) {
            logMessages = JSON.parse(storedLogs);
            logContainer.innerHTML = `<strong>로그:</strong><br>` + logMessages.join('<br>');
            logContainer.scrollTop = logContainer.scrollHeight;
        }
    }

    function updateFilterPatterns() {
        if (!filteringEnabled) {
            filterPatterns = [];
            log(`🛠 필터링 기능 비활성화됨.`);
            return;
        }
        const input = filterInput.value.trim();
        filterPatterns = input ? input.split(',').map(word => {
            word = word.trim();
            if (word.startsWith('/') && word.endsWith('/')) {
                try {
                    return new RegExp(word.slice(1, -1), 'i');
                } catch (e) {
                    log(`⚠️ 유효하지 않은 정규식: ${word}`);
                    return null;
                }
            } else {
                return new RegExp(word, 'i');
            }
        }).filter(regex => regex !== null) : [];
        log(`🛠 필터 패턴 업데이트: [${filterPatterns.map(r => r.toString()).join(', ')}]`);
    }

    function updateExcludeDomains() {
        // 이 함수는 제외 도메인이 추가될 때마다 호출됨
        log(`🚫 제외 도메인 업데이트 완료.`);
    }

    function addExcludeDomain() {
        const domain = excludeDomainsInput.value.trim().toLowerCase();
        if (domain && !excludeDomains.includes(domain)) {
            excludeDomains.push(domain);
            renderExcludeDomains();
            excludeDomainsInput.value = '';
            log(`🚫 제외 도메인 추가: ${domain}`);
        }
    }

    function renderExcludeDomains() {
        excludeDomainsList.innerHTML = '';
        excludeDomains.forEach((domain, index) => {
            const li = document.createElement('li');
            li.innerHTML = `
                <span>${escapeHTML(domain)}</span>
                <button data-index="${index}">✖️</button>
            `;
            excludeDomainsList.appendChild(li);
        });
        // 삭제 버튼 이벤트 리스너
        excludeDomainsList.querySelectorAll('button').forEach(btn => {
            btn.addEventListener('click', () => {
                const index = parseInt(btn.getAttribute('data-index'));
                if (!isNaN(index)) {
                    const removed = excludeDomains.splice(index, 1);
                    renderExcludeDomains();
                    log(`🚫 제외 도메인 제거: ${removed}`);
                }
            });
        });
    }

    function loadExcludeDomains() {
        const storedDomains = GM_getValue('excludeDomains', []);
        excludeDomains = storedDomains.length > 0 ? storedDomains : [];
        renderExcludeDomains();
    }

    function updateSiteFilterPatterns() {
        const input = siteFilterInput.value.trim();
        siteFilterPatterns = input ? input.split(',').map(pattern => {
            pattern = pattern.trim();
            if (pattern.startsWith('/') && pattern.endsWith('/')) {
                try {
                    return new RegExp(pattern.slice(1, -1), 'i');
                } catch (e) {
                    log(`⚠️ 유효하지 않은 사이트 필터 정규식: ${pattern}`);
                    return null;
                }
            } else {
                return new RegExp(pattern, 'i');
            }
        }).filter(regex => regex !== null) : [];
        log(`🛠 사이트 필터 패턴 업데이트: [${siteFilterPatterns.map(r => r.toString()).join(', ')}]`);
    }

    function updateDuplicateThreshold() {
        const value = parseInt(duplicateThresholdInput.value);
        duplicateThreshold = isNaN(value) || value < 1 ? 5 : value;
        log(`🔄 중복 허용 횟수 설정: ${duplicateThreshold}`);
    }

    function updateRetryAttempts() {
        const value = parseInt(retryAttemptsInput.value);
        retryAttempts = isNaN(value) || value < 1 ? 10 : value;
        log(`🔄 재시도 횟수 설정: ${retryAttempts}`);
    }

    function updateRetryInterval() {
        const value = parseInt(retryIntervalInput.value);
        retryInterval = isNaN(value) || value < 100 ? 100 : value; // 최소 값 100ms으로 설정
        log(`🔄 재시도 간격 설정: ${retryInterval}ms`);
    }

    function updateStrictSiteFilter() {
        const isStrict = strictSiteFilter.checked;
        log(`🔄 엄격 사이트 필터링 설정: ${isStrict ? 'ON' : 'OFF'}`);
    }

    function toggleFiltering() {
        filteringEnabled = filteringToggle.checked;
        if (filteringEnabled) {
            log('🔄 필터링 기능 활성화됨.');
            updateFilterPatterns();
        } else {
            filterPatterns = [];
            log('🔄 필터링 기능 비활성화됨.');
        }
    }

    function toggleDarkweb() {
        darkwebEnabled = darkwebToggle.checked;
        log(`🔄 다크웹 검색 엔진 사용 설정: ${darkwebEnabled ? 'ON' : 'OFF'}`);
    }

    function getSearchQueryAndEngine() {
        const url = window.location.href;
        if (url.includes('google.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'google';
        } else if (url.includes('bing.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'bing';
        } else if (url.includes('daum.net/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'daum';
        } else if (url.includes('search.naver.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('query') || '';
            searchEngine = 'naver';
        } else if (url.includes('search.yahoo.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('p') || '';
            searchEngine = 'yahoo';
        } else if (url.includes('duckduckgo.com/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'duckduckgo';
        } else if (url.includes('ecosia.org/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'ecosia';
        } else if (url.includes('.onion')) {
            // 다크웹 검색 엔진 중 하나로 설정
            searchEngine = 'darkweb';
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            if (!searchQuery) {
                searchQuery = prompt('다크웹 검색어를 입력하세요:');
            }
        } else {
            searchQuery = '';
            searchEngine = '';
        }
        log(`🔍 검색 엔진: ${searchEngine}, 검색어: "${searchQuery}"`);
    }

    async function startCrawling() {
        if (isRunning) {
            alert('이미 크롤링이 진행 중입니다.');
            return;
        }
        getSearchQueryAndEngine();
        if (!searchQuery) {
            alert('현재 페이지에서 검색어를 찾을 수 없습니다.');
            log('⚠️ 검색어 미입력 또는 인식 실패.');
            return;
        }
        isRunning = true;
        isPaused = false;
        currentPage = 1;
        results = [];
        visitedLinks.clear();
        resultsBody.innerHTML = '';
        visitedCountElem.innerText = '0';
        elapsedTimeElem.innerText = '00:00:00';
        remainingTimeElem.innerText = '00:00:00';
        logMessages = [];
        logContainer.innerHTML = `<strong>로그:</strong><br>`;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
        log('✅ 크롤링을 시작합니다.');
        updateDuplicateThreshold();
        updateRetryAttempts();
        updateRetryInterval();
        await enqueueCrawlPages(concurrentRequests); // 동시 요청 수 40으로 설정
        startTime = Date.now();
        elapsedInterval = setInterval(updateElapsedTime, 1000);
    }

    function togglePause() {
        if (!isRunning) {
            alert('일시정지/재개할 크롤링이 없습니다.');
            return;
        }
        isPaused = !isPaused;
        if (isPaused) {
            log('⏸️ 크롤링을 일시정지하였습니다.');
            pauseBtn.innerHTML = '<i>▶️</i> 재개';
        } else {
            log('▶️ 크롤링을 재개하였습니다.');
            pauseBtn.innerHTML = '<i>⏸️</i> 일시정지';
            processQueue();
        }
    }

    function stopCrawling() {
        if (!isRunning) {
            alert('중지할 크롤링이 없습니다.');
            return;
        }
        isRunning = false;
        isPaused = false;
        pauseBtn.innerHTML = '<i>⏸️</i> 일시정지';
        clearInterval(elapsedInterval);
        updateProgress();
        log('🚫 크롤링을 중지하였습니다.');
        GM_notification({
            text: '크롤링이 중지되었습니다.',
            title: '웹 크롤러 7.9.5',
            timeout: 3000
        });
    }

    async function enqueueCrawlPages(limit) {
        for (let i = 0; i < limit; i++) {
            queueCrawlPage();
        }
    }

    function queueCrawlPage() {
        if (!isRunning || isPaused) return;
        if (currentRequests >= concurrentRequests) return;

        // Assign a unique page number before starting the crawl
        const pageToCrawl = currentPage++;
        currentPageElem.innerText = currentPage;

        currentRequests++;
        crawlPage(pageToCrawl).then(() => {
            currentRequests--;
            queueCrawlPage();
        });
    }

    async function crawlPage(page) {
        if (!isRunning || isPaused) {
            return;
        }

        // 검색 엔진에 따른 URL 구성
        let url = '';
        if (searchEngine === 'google') {
            url = `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}&start=${(page-1)*10}`;
        } else if (searchEngine === 'bing') {
            url = `https://www.bing.com/search?q=${encodeURIComponent(searchQuery)}&first=${(page-1)*10 +1}`;
        } else if (searchEngine === 'daum') {
            url = `https://search.daum.net/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'naver') {
            url = `https://search.naver.com/search.naver?query=${encodeURIComponent(searchQuery)}&start=${(page-1)*10 +1}`;
        } else if (searchEngine === 'yahoo') {
            url = `https://search.yahoo.com/search?p=${encodeURIComponent(searchQuery)}&b=${(page-1)*10 +1}`;
        } else if (searchEngine === 'duckduckgo') {
            url = `https://duckduckgo.com/html/?q=${encodeURIComponent(searchQuery)}&s=${(page-1)*10}`;
        } else if (searchEngine === 'ecosia') {
            url = `https://www.ecosia.org/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'darkweb') {
            if (!darkwebEnabled) {
                log('🛑 다크웹 검색 엔진이 비활성화되어 있습니다.');
                isRunning = false;
                return;
            }
            const darkwebEngines = getDarkwebEngines();
            if (darkwebEngines.length === 0) {
                log('⚠️ 다크웹 검색 엔진이 설정되지 않았습니다.');
                isRunning = false;
                return;
            }
            const randomEngine = darkwebEngines[Math.floor(Math.random() * darkwebEngines.length)];
            url = `${randomEngine.url}/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else {
            alert('지원하지 않는 검색 엔진입니다.');
            log('❌ 지원하지 않는 검색 엔진.');
            isRunning = false;
            return;
        }

        log(`📄 페이지 ${page} 크롤링 중: ${url}`);

        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                headers: {
                    "User-Agent": navigator.userAgent,
                    "Accept": "text/html"
                },
                onload: async function(response) {
                    if (response.status !== 200) {
                        log(`❌ 페이지 ${page} 로드 실패: 상태 ${response.status}`);
                        await handleCrawlError(page, retryAttempts);
                        resolve();
                        return;
                    }

                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');
                    let items = [];
                    if (searchEngine === 'google') {
                        items = doc.querySelectorAll('div.g');
                    } else if (searchEngine === 'bing') {
                        items = doc.querySelectorAll('li.b_algo');
                    } else if (searchEngine === 'daum') {
                        items = doc.querySelectorAll('div.wrap_cont');
                    } else if (searchEngine === 'naver') {
                        items = doc.querySelectorAll('div.basic1');
                    } else if (searchEngine === 'yahoo') {
                        items = doc.querySelectorAll('div.compTitle');
                    } else if (searchEngine === 'duckduckgo') {
                        items = doc.querySelectorAll('div.result');
                    } else if (searchEngine === 'ecosia') {
                        items = doc.querySelectorAll('div.result');
                    } else if (searchEngine === 'darkweb') {
                        // 다크웹 검색 엔진 중 하나로 설정 (예: Torch 기준)
                        items = doc.querySelectorAll('.result'); // 실제 검색 엔진에 맞게 조정 필요
                    }

                    // 만약 검색 결과가 없다면 크롤링 종료
                    if (items.length === 0) {
                        log('🔍 더 이상 크롤링할 페이지가 없습니다. 크롤링을 종료합니다.');
                        isRunning = false;
                        isPaused = false;
                        pauseBtn.innerHTML = '<i>⏸️</i> 일시정지';
                        clearInterval(elapsedInterval);
                        updateProgress();
                        GM_notification({
                            text: '크롤링이 완료되었습니다.',
                            title: '웹 크롤러 7.9.5',
                            timeout: 3000
                        });
                        resolve();
                        return;
                    }

                    log(`📊 페이지 ${page}에서 ${items.length}개 항목 발견.`);

                    // 병렬 처리: Promise.all을 이용하여 비동기적으로 처리
                    const itemPromises = Array.from(items).map(async (item) => {
                        let title = 'N/A';
                        let link = 'N/A';
                        let description = 'N/A';
                        let image = 'N/A';
                        let htmlLink = '비활성화';
                        let pageLoadTime = 'N/A';

                        // 각 검색 엔진에 따른 데이터 추출
                        if (searchEngine === 'google') {
                            const titleElem = item.querySelector('h3');
                            const linkElem = item.querySelector('a');
                            const descElem = item.querySelector('.IsZvec') || item.querySelector('.VwiC3b');
                            if (titleElem) title = titleElem.innerText;
                            if (linkElem) link = linkElem.href;
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'bing') {
                            const titleElem = item.querySelector('h2 a');
                            const descElem = item.querySelector('p');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'daum') {
                            const titleElem = item.querySelector('a.link_fax');
                            const descElem = item.querySelector('div.cont_inner');
                            if (titleElem) {
                                title = titleElem.innerText.trim();
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText.trim();

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'naver') {
                            const titleElem = item.querySelector('a.tit');
                            const descElem = item.querySelector('div.desc');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img.thumb');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'yahoo') {
                            const titleElem = item.querySelector('h3');
                            const linkElem = item.querySelector('a');
                            const descElem = item.querySelector('div.compText');
                            if (titleElem) title = titleElem.innerText;
                            if (linkElem) link = linkElem.href;
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'duckduckgo') {
                            const titleElem = item.querySelector('a.result__a');
                            const descElem = item.querySelector('a.result__snippet');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img.result__thumbnail');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'ecosia') {
                            const titleElem = item.querySelector('a.result-title');
                            const descElem = item.querySelector('div.result-description');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img.result-image');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        } else if (searchEngine === 'darkweb') {
                            // 다크웹 검색 결과 추출 (예: Torch 기준)
                            const titleElem = item.querySelector('a');
                            const descElem = item.querySelector('p');
                            if (titleElem) {
                                title = titleElem.innerText;
                                link = titleElem.href;
                            }
                            if (descElem) description = descElem.innerText;

                            // 이미지 가져오기 (가능한 경우)
                            const imgElem = item.querySelector('img');
                            if (imgElem && imgElem.src) {
                                image = `<img src="${imgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                            }

                            // HTML 보기 링크
                            if (toggleHTML.checked && link !== 'N/A') {
                                htmlLink = `<button class="html-btn" data-url="${encodeURIComponent(link)}">보기</button>`;
                            }
                        }

                        // 광고 제외 기능 적용
                        if (toggleAds.checked) {
                            const combinedText = `${title} ${description}`.toLowerCase();
                            const isAd = combinedText.includes('advertisement') || combinedText.includes('광고');
                            if (isAd) {
                                log(`🚫 광고 항목 제외: "${title}"`);
                                return null;
                            }
                        }

                        // 필터 단어 적용
                        if (filterPatterns.length > 0) {
                            const combinedText = `${title} ${description}`.toLowerCase();
                            const isFiltered = filterPatterns.some(regex => regex.test(combinedText));
                            if (isFiltered) {
                                log(`❌ 필터 단어 포함 항목 제외: "${title}"`);
                                return null;
                            }
                        }

                        // 사이트 필터링 적용 (사이트 제목 또는 HTML 내용 기반)
                        if (siteFilterPatterns.length > 0) {
                            const isSiteFiltered = siteFilterPatterns.some(regex => regex.test(link) || regex.test(title));
                            if (isSiteFiltered) {
                                log(`🚫 사이트 필터링에 의해 제외된 항목: "${link}"`);
                                return null;
                            }
                        }

                        // 도메인 추출
                        const domain = extractDomain(link);
                        if (!domain) return null;

                        // 제외 도메인 검사
                        if (excludeDomains.includes(domain.toLowerCase())) {
                            log(`🚫 제외 도메인 항목 제외: "${link}"`);
                            return null;
                        }

                        // 엄격 사이트 필터링 적용
                        if (strictSiteFilter.checked) {
                            if (!siteFilterPatterns.some(regex => regex.test(link) || regex.test(title))) {
                                log(`🚫 엄격 사이트 필터링에 의해 제외된 항목: "${link}"`);
                                return null;
                            }
                        }

                        // 중복 검사 (도메인별 방문 횟수)
                        let visitCount = visitedLinks.get(domain) || 0;
                        if (visitCount >= duplicateThreshold) {
                            log(`🔄 중복 방문 제한: "${domain}"`);
                            return null;
                        }

                        // 방문 횟수 업데이트
                        visitedLinks.set(domain, visitCount + 1);
                        visitedCountElem.innerText = visitedLinks.size.toString();

                        // 페이지 로딩 시간 측정
                        const loadStart = performance.now();
                        try {
                            // mode: 'cors'로 변경하여 실제 응답을 받을 수 있도록 함
                            const fetchResponse = await fetch(link, { mode: 'cors' });
                            if (fetchResponse.ok) {
                                const loadEnd = performance.now();
                                pageLoadTime = Math.round(loadEnd - loadStart);
                            } else {
                                pageLoadTime = '응답 불량';
                            }
                        } catch (err) {
                            pageLoadTime = '계산 불가';
                        }

                        // HTML 옵션
                        if (!toggleHTML.checked) {
                            htmlLink = '비활성화';
                        }

                        // 이미지 옵션
                        if (!toggleImages.checked) {
                            image = '비활성화';
                        }

                        const result = { title, link, description, image, htmlLink, pageLoadTime };
                        results.push(result);

                        // 결과 테이블에 추가
                        const row = document.createElement('tr');
                        row.innerHTML = `
                            <td>${escapeHTML(title)}</td>
                            <td><a href="${escapeHTML(link)}" target="_blank">${escapeHTML(link)}</a></td>
                            <td>${escapeHTML(description)}</td>
                            <td>${image}</td>
                            <td>${htmlLink}</td>
                            <td>${pageLoadTime}</td>
                        `;
                        resultsBody.appendChild(row);

                        return result;
                    });

                    // Promise.all을 통해 모든 항목 처리 완료 대기
                    await Promise.all(itemPromises);

                    // 데이터 시각화 업데이트
                    updateChart();

                    // 진행률 업데이트
                    updateProgress();

                    // 대기 시간 추가하여 서버에 부담 주지 않기
                    setTimeout(() => {
                        queueCrawlPage();
                    }, isPaused ? 0 : 1000); // 1초 대기

                    resolve();
                },
                onerror: function() {
                    log(`❌ 페이지 ${page} 요청 중 오류 발생.`);
                    handleCrawlError(page, retryAttempts).then(() => {
                        resolve();
                    });
                }
            });
        });
    }

    async function handleCrawlError(page, retriesLeft) {
        if (retriesLeft > 0 && isRunning) {
            log(`🔄 페이지 ${page} 재시도 (${retriesLeft}회 남음).`);
            return new Promise((resolve) => {
                setTimeout(() => {
                    const url = constructURL(page);
                    if (!url) {
                        log(`❌ 페이지 ${page} 재시도 실패: URL이 유효하지 않습니다.`);
                        resolve();
                        return;
                    }
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: url,
                        headers: {
                            "User-Agent": navigator.userAgent,
                            "Accept": "text/html"
                        },
                        onload: async function(response) {
                            if (response.status === 200) {
                                log(`✅ 페이지 ${page} 재시도 성공.`);
                                await crawlPage(page);
                            } else {
                                await handleCrawlError(page, retriesLeft - 1);
                            }
                            resolve();
                        },
                        onerror: async function() {
                            await handleCrawlError(page, retriesLeft - 1);
                            resolve();
                        }
                    });
                }, retryInterval); // retryInterval이 100ms으로 설정됨
            });
        } else {
            log(`❌ 페이지 ${page} 로드 실패: 최대 재시도 횟수 초과.`);
            // 크롤링을 중지하지 않고 다음 페이지로 넘어감
            currentPage++;
            currentPageElem.innerText = currentPage;
            setTimeout(() => {
                queueCrawlPage();
            }, 1000);
            return Promise.resolve();
        }
    }

    function constructURL(page) {
        if (searchEngine === 'google') {
            return `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}&start=${(page-1)*10}`;
        } else if (searchEngine === 'bing') {
            return `https://www.bing.com/search?q=${encodeURIComponent(searchQuery)}&first=${(page-1)*10 +1}`;
        } else if (searchEngine === 'daum') {
            return `https://search.daum.net/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'naver') {
            return `https://search.naver.com/search.naver?query=${encodeURIComponent(searchQuery)}&start=${(page-1)*10 +1}`;
        } else if (searchEngine === 'yahoo') {
            return `https://search.yahoo.com/search?p=${encodeURIComponent(searchQuery)}&b=${(page-1)*10 +1}`;
        } else if (searchEngine === 'duckduckgo') {
            return `https://duckduckgo.com/html/?q=${encodeURIComponent(searchQuery)}&s=${(page-1)*10}`;
        } else if (searchEngine === 'ecosia') {
            return `https://www.ecosia.org/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else if (searchEngine === 'darkweb') {
            const darkwebEngines = getDarkwebEngines();
            if (darkwebEngines.length === 0) {
                return '';
            }
            const randomEngine = darkwebEngines[Math.floor(Math.random() * darkwebEngines.length)];
            return `${randomEngine.url}/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
        } else {
            return '';
        }
    }

    function updateProgress() {
        // 무제한이므로 진행률을 페이지 수에 기반하여 표시
        const progressPercent = (currentPage % 100) * 1; // 100페이지마다 100%로 리셋
        progressBar.value = progressPercent > 100 ? 100 : progressPercent;
        progressText.innerText = `페이지 ${currentPage}`;
        if (progressPercent >= 100) {
            progressBar.value = 0;
        }
    }

    function resetCrawling() {
        isRunning = false;
        isPaused = false;
        currentPage = 1;
        results = [];
        visitedLinks.clear();
        resultsBody.innerHTML = '';
        progressBar.value = 0;
        progressText.innerText = '페이지 0';
        currentPageElem.innerText = '0';
        visitedCountElem.innerText = '0';
        elapsedTimeElem.innerText = '00:00:00';
        remainingTimeElem.innerText = '00:00:00';
        logMessages = [];
        logContainer.innerHTML = `<strong>로그:</strong><br>`;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
        // 차트 초기화
        crawlData.labels = [];
        crawlData.datasets[0].data = [];
        if (crawlChart) {
            crawlChart.update();
        }
        clearInterval(elapsedInterval);
        log('🔄 크롤링을 초기화하였습니다.');
    }

    function exportResults() {
        if (results.length === 0) {
            alert('내보낼 결과가 없습니다.');
            return;
        }
        const format = prompt('내보낼 형식을 선택하세요 (xlsx, csv, json):', 'xlsx');
        if (!format) return;
        if (format.toLowerCase() === 'xlsx') {
            const ws = XLSX.utils.json_to_sheet(results);
            const wb = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(wb, ws, "결과");
            XLSX.writeFile(wb, "크롤링_결과.xlsx");
            log('💾 결과를 "크롤링_결과.xlsx" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.xlsx" 파일로 내보냈습니다.',
                title: '웹 크롤러 7.9.5',
                timeout: 3000
            });
        } else if (format.toLowerCase() === 'csv') {
            const csv = results.map(row => `"${row.title.replace(/"/g, '""')}","${row.link.replace(/"/g, '""')}","${row.description.replace(/"/g, '""')}","${row.image.replace(/"/g, '""')}","${row.htmlLink.replace(/"/g, '""')}","${row.pageLoadTime}"`).join('\n');
            const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = '크롤링_결과.csv';
            a.click();
            log('💾 결과를 "크롤링_결과.csv" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.csv" 파일로 내보냈습니다.',
                title: '웹 크롤러 7.9.5',
                timeout: 3000
            });
        } else if (format.toLowerCase() === 'json') {
            const json = JSON.stringify(results, null, 2);
            const blob = new Blob([json], { type: 'application/json;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = '크롤링_결과.json';
            a.click();
            log('💾 결과를 "크롤링_결과.json" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.json" 파일로 내보냈습니다.',
                title: '웹 크롤러 7.9.5',
                timeout: 3000
            });
        } else {
            alert('지원하지 않는 형식입니다.');
        }
    }

    function escapeHTML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    function extractDomain(url) {
        try {
            const urlObj = new URL(url);
            return urlObj.hostname.replace('www.', '');
        } catch (e) {
            return null;
        }
    }

    function updateElapsedTime() {
        if (!startTime) return;
        const now = Date.now();
        const elapsed = Math.floor((now - startTime) / 1000);
        elapsedTimeElem.innerText = formatTime(elapsed);
        const estimatedTotal = currentPage * 1; // 예측: 각 페이지 1초
        const remaining = Math.max(estimatedTotal - elapsed, 0);
        remainingTimeElem.innerText = formatTime(remaining);
    }

    function formatTime(seconds) {
        const hrs = String(Math.floor(seconds / 3600)).padStart(2, '0');
        const mins = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
        const secs = String(seconds % 60).padStart(2, '0');
        return `${hrs}:${mins}:${secs}`;
    }

    function updateChart() {
        crawlData.labels = Array.from(visitedLinks.keys());
        crawlData.datasets[0].data = Array.from(visitedLinks.values());

        if (crawlChart) {
            crawlChart.update();
        }
        // 저장
        GM_setValue('crawlerData', JSON.stringify(crawlData));
    }

    function loadCrawlData() {
        const storedData = GM_getValue('crawlerData', null);
        if (storedData) {
            const parsedData = JSON.parse(storedData);
            crawlData = parsedData;
            if (crawlChart) {
                crawlChart.data = crawlData;
                crawlChart.update();
            }
        }
    }

    function saveCrawlData() {
        GM_setValue('crawlerData', JSON.stringify(crawlData));
    }

    // 주기적으로 크롤링 데이터를 저장
    setInterval(saveCrawlData, 5000);

    // 검색 엔진 바로 가기 버튼 기능
    function openSearchEngines() {
        const engineUrls = {
            'Google': 'https://www.google.com/',
            'Bing': 'https://www.bing.com/',
            'Daum': 'https://www.daum.net/',
            'Naver': 'https://www.naver.com/',
            'Yahoo': 'https://www.yahoo.com/',
            'DuckDuckGo': 'https://duckduckgo.com/',
            'Ecosia': 'https://www.ecosia.org/',
            'Yahoo Japan': 'https://www.yahoo.co.jp/',
            'Baidu': 'https://www.baidu.com/',
            'Yandex': 'https://www.yandex.com/',
            'Ask': 'https://www.ask.com/',
            'Startpage': 'https://www.startpage.com/',
            'Qwant': 'https://www.qwant.com/',
            'Swisscows': 'https://swisscows.com/',
            'Mojeek': 'https://www.mojeek.com/',
            'Gibiru': 'https://www.gibiru.com/',
            'MetaGer': 'https://metager.org/',
            'Peekier': 'https://peekier.com/',
            'Disconnect Search': 'https://search.disconnect.me/',
            'Ekoru': 'https://www.ekoru.org/',
            'Yippy': 'https://yippy.com/',
            'Infinity Search': 'https://www.infinitysearch.co/',
            'WolframAlpha': 'https://www.wolframalpha.com/',
            'Shodan': 'https://www.shodan.io/',
            'Archive.org': 'https://archive.org/',
            'Wayback Machine': 'https://web.archive.org/',
            'PubMed': 'https://pubmed.ncbi.nlm.nih.gov/',
            'IEEE Xplore': 'https://ieeexplore.ieee.org/',
            'Google Scholar': 'https://scholar.google.com/',
            'LexisNexis': 'https://www.lexisnexis.com/',
            'Google Books': 'https://books.google.com/',
            'Creative Commons Search': 'https://search.creativecommons.org/',
            'TOR Search': 'http://torsearch7uu5ond.onion/',
            'Ahmia': 'http://msydqstlz2kzerdg.onion/',
            'Haystak': 'http://haystakvxad7wbk5.onion/',
            'Dark Search': 'http://darksearch3w3nrc.onion/',
            'Phobos': 'http://phobos6uj4abm4pf.onion/',
            'Not Evil': 'http://hss3uro2hsxfogfq.onion/',
            'IntelligenceX': 'http://intelligencexmh5v.onion/',
            'Torch': 'http://xmh57jrzrnw6insl.onion/',
            'Silk Road 3.1': 'http://silkroad31lo5ya.onion/',
            'Fresh Onions': 'http://rx7hmtf7k7xk6ked.onion/',
            'Dark Web Link': 'http://darkweblink5wqk.onion/',
            'Tor Tracker': 'http://tortracker5ujzra.onion/',
            'Deep Web Search': 'http://deepwebsearch6b6x.onion/',
            'Onion Monitor': 'http://onionmonitor3c7v.onion/',
            'Hidden Wiki': 'http://hiddenwiki5z35.onion/',
            'TorEX': 'http://torexk3wd2buz.onion/',
            'BlackBook': 'http://blackbook51uv.onion/',
            'Taboo Search': 'http://taboosearch37b.onion/',
            'The Pirate Bay (onion)': 'http://uj3wazyk5u4hnvtk.onion/',
            'Dogpile': 'https://www.dogpile.com/',
            'Oscobo': 'https://www.oscobo.com/',
            'HotBot': 'https://www.hotbot.com/',
            'Peekier': 'https://peekier.com/',
            'Lukol': 'https://www.lukol.com/',
            'Privatelee': 'https://privatelee.com/',
            'DuckDuckGo (Tor)': 'http://3g2upl4pq6kufc4m.onion/',
            'TorEX': 'http://torexk3wd2buz.onion/',
            'BlackBook': 'http://blackbook51uv.onion/',
            'Taboo Search': 'http://taboosearch37b.onion/',
            'Kilos': 'http://kilos7wbfn3ptgw.onion/',
            'Candle': 'http://zg6ak7qqfhbkbppx.onion/',
            'Dark Falcon': 'http://darkfalconphp5fh.onion/',
            'Hidden Wiki': 'http://hiddenwiki5z35.onion/',
            'Onion Monitor': 'http://onionmonitor3c7v.onion/',
            'Phobos 2': 'http://phobos2uj4abm4pf.onion/',
            'Deep Web Search': 'http://deepwebsearch6b6x.onion/',
            'DarkSearchBox': 'http://darksearchbox5a3x.onion/',
            'Black Search': 'http://blacksearch7xm7.onion/',
            'Search Encrypt': 'https://www.searchencrypt.com/',
            'Peekier': 'https://peekier.com/',
            'Lukol': 'https://www.lukol.com/',
            'Disconnect Search': 'https://search.disconnect.me/',
            'Privatelee': 'https://privatelee.com/',
            'Infinity Search': 'https://www.infinitysearch.co/',
            'Dogpile': 'https://www.dogpile.com/',
            'Startpage': 'https://www.startpage.com/',
            'Exalead': 'https://www.exalead.com/',
            'Gigablast': 'https://www.gigablast.com/',
            'Searx': 'https://searx.me/',
            'FindSounds': 'https://www.findsounds.com/',
            'TinEye': 'https://tineye.com/',
            'Boardreader': 'https://www.boardreader.com/',
            'PubCrawl': 'https://pubcrawl.org/',
            'BeautyFetch': 'http://beautyfetch.com/',
            'WolframAlpha': 'https://www.wolframalpha.com/',
            'Shodan': 'https://www.shodan.io/',
            'Archive.org': 'https://archive.org/',
            'Wayback Machine': 'https://web.archive.org/',
            'PubMed': 'https://pubmed.ncbi.nlm.nih.gov/',
            'IEEE Xplore': 'https://ieeexplore.ieee.org/',
            'Google Scholar': 'https://scholar.google.com/',
            'LexisNexis': 'https://www.lexisnexis.com/',
            'Google Books': 'https://books.google.com/',
            'Creative Commons Search': 'https://search.creativecommons.org/',
            // 추가 엔진 가능
        };
        let html = '📡 <strong>검색 엔진:</strong><br>';
        for (const [name, url] of Object.entries(engineUrls)) {
            html += `<button class="search-engine-btn" data-url="${url}">${name}</button><br>`;
        }
        // 팝업 생성
        const popup = document.createElement('div');
        popup.className = 'search-engines-popup';
        popup.innerHTML = html;
        container.appendChild(popup);
        popup.style.display = 'block';

        // 버튼 이벤트 리스너
        popup.querySelectorAll('.search-engine-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                window.open(btn.getAttribute('data-url'), '_blank');
                popup.style.display = 'none';
            });
        });

        // 외부 클릭 시 팝업 닫기
        document.addEventListener('click', function(event) {
            if (!popup.contains(event.target) && event.target !== searchEnginesBtn) {
                popup.style.display = 'none';
            }
        }, { once: true });
    }

    // HTML 탭에 콘텐츠 표시 기능
    container.addEventListener('click', function(event) {
        if (event.target.classList.contains('html-btn')) {
            const url = decodeURIComponent(event.target.getAttribute('data-url'));
            showHTMLInUI(url);
        }
    });

    container.addEventListener('mouseover', function(event) {
        if (event.target.classList.contains('html-btn')) {
            const url = decodeURIComponent(event.target.getAttribute('data-url'));
            showPreviewPopup(event.target, url);
        }
    });

    container.addEventListener('mouseout', function(event) {
        if (event.target.classList.contains('html-btn')) {
            const popup = container.querySelector('.preview-popup');
            if (popup) {
                popup.style.display = 'none';
            }
        }
    });

    function showHTMLInUI(url) {
        // UI 내에 iframe을 사용하여 사이트 내용 표시
        let iframe = container.querySelector('.html-display iframe');
        if (!iframe) {
            iframe = document.createElement('iframe');
            container.querySelector('.html-display').appendChild(iframe);
        }
        iframe.src = url;
        // 탭을 'HTML'로 전환
        switchTab('html-display');
        log(`🔍 HTML 보기: ${url}`);
    }

    function showPreviewPopup(target, url) {
        // 팝업 생성
        let popup = container.querySelector('.preview-popup');
        if (!popup) {
            popup = document.createElement('div');
            popup.className = 'preview-popup';
            popup.innerHTML = `<iframe src="${url}" style="width:100%; height:100%; border:none;"></iframe>`;
            container.appendChild(popup);
        }
        popup.style.display = 'block';
        // 위치 조정
        const rect = target.getBoundingClientRect();
        const containerRect = container.getBoundingClientRect();
        popup.style.left = `${rect.left - containerRect.left + 20}px`;
        popup.style.top = `${rect.bottom - containerRect.top + 5}px`;
    }

    function switchTab(tabId) {
        const tabs = container.querySelectorAll('.tab');
        const tabContents = container.querySelectorAll('.tab-content');

        tabs.forEach(t => {
            if(t.getAttribute('data-tab') === tabId){
                t.classList.add('active');
            } else {
                t.classList.remove('active');
            }
        });

        tabContents.forEach(content => {
            if(content.id === tabId){
                content.classList.add('active');
            } else {
                content.classList.remove('active');
            }
        });
    }

    // 다크웹 검색 엔진 관리
    function loadDarkwebEngines() {
        const storedEngines = GM_getValue('darkwebEngines', defaultDarkwebEngines);
        darkwebEnginesList.innerHTML = '';
        storedEngines.forEach(engine => {
            const li = document.createElement('li');
            li.innerHTML = `
                <span>${escapeHTML(engine.name)}</span>
                <button class="remove-darkweb-engine-btn" data-url="${engine.url}">🗑️</button>
            `;
            darkwebEnginesList.appendChild(li);
        });

        // 삭제 버튼 이벤트 리스너
        darkwebEnginesList.querySelectorAll('.remove-darkweb-engine-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                const url = btn.getAttribute('data-url');
                removeDarkwebEngine(url);
            });
        });
    }

    function addDarkwebEngine() {
        const name = prompt('다크웹 검색 엔진 이름을 입력하세요:');
        if (!name) {
            alert('이름을 입력하지 않았습니다.');
            return;
        }
        const url = prompt('다크웹 검색 엔진 URL을 입력하세요 (예: http://exampleonion.onion/):');
        if (!url) {
            alert('URL을 입력하지 않았습니다.');
            return;
        }
        const newEngine = { name, url };
        const storedEngines = GM_getValue('darkwebEngines', defaultDarkwebEngines);
        storedEngines.push(newEngine);
        GM_setValue('darkwebEngines', storedEngines);
        loadDarkwebEngines();
        log(`🔄 다크웹 검색 엔진 추가: ${name} (${url})`);
    }

    function removeDarkwebEngine(url) {
        let storedEngines = GM_getValue('darkwebEngines', defaultDarkwebEngines);
        storedEngines = storedEngines.filter(engine => engine.url !== url);
        GM_setValue('darkwebEngines', storedEngines);
        loadDarkwebEngines();
        log(`🔄 다크웹 검색 엔진 제거: ${url}`);
    }

    function getDarkwebEngines() {
        return GM_getValue('darkwebEngines', defaultDarkwebEngines);
    }

    // 번역기 기능 추가
    async function translateContent() {
        if (results.length === 0) {
            alert('번역할 결과가 없습니다.');
            return;
        }
        translatedContent.innerHTML = '번역 중...';

        try {
            // i18next를 이용한 번역
            const texts = results.map(r => r.description).join('. ');
            const translatedText = await translateText(texts, translateLanguage);
            translatedContent.innerText = translatedText;
            log('🔄 번역이 완료되었습니다.');
        } catch (error) {
            translatedContent.innerText = '번역 중 오류가 발생했습니다.';
            log(`❌ 번역 오류: ${error}`);
        }
    }

    async function translateText(text, targetLang) {
        // Papago 번역 API 사용 (API 키 필요)
        const apiUrl = 'https://openapi.naver.com/v1/papago/n2mt';
        const clientId = 'YOUR_NAVER_CLIENT_ID'; // [필수] 실제 클라이언트 ID로 교체하세요.
        const clientSecret = 'YOUR_NAVER_CLIENT_SECRET'; // [필수] 실제 클라이언트 시크릿으로 교체하세요.

        const data = new URLSearchParams();
        data.append('source', 'ko');
        data.append('target', targetLang);
        data.append('text', text);

        const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'X-Naver-Client-Id': clientId,
                'X-Naver-Client-Secret': clientSecret
            },
            body: data
        });

        const result = await response.json();
        if (result.errorMessage) {
            throw new Error(result.errorMessage);
        }
        return result.message.result.translatedText;
    }

    // 번역기 섹션 버튼 클릭 처리
    // 번역 기능은 위에서 정의되었습니다.

    // 로깅, 필터링, 크롤링 함수들은 이미 위에서 상세히 구현되었습니다.

    // 검색 엔진 바로가기 직접 UI에 표시
    function loadSearchEnginesDirect() {
        const engineUrls = {
            'Google': 'https://www.google.com/',
            'Bing': 'https://www.bing.com/',
            'Daum': 'https://www.daum.net/',
            'Naver': 'https://www.naver.com/',
            'Yahoo': 'https://www.yahoo.com/',
            'DuckDuckGo': 'https://duckduckgo.com/',
            'Ecosia': 'https://www.ecosia.org/',
            'Yahoo Japan': 'https://www.yahoo.co.jp/',
            'Baidu': 'https://www.baidu.com/',
            'Yandex': 'https://www.yandex.com/',
            'Ask': 'https://www.ask.com/',
            'Startpage': 'https://www.startpage.com/',
            'Qwant': 'https://www.qwant.com/',
            'Swisscows': 'https://swisscows.com/',
            'Mojeek': 'https://www.mojeek.com/',
            'Gibiru': 'https://www.gibiru.com/',
            'MetaGer': 'https://metager.org/',
            'Peekier': 'https://peekier.com/',
            'Disconnect Search': 'https://search.disconnect.me/',
            'Ekoru': 'https://www.ekoru.org/',
            'Yippy': 'https://yippy.com/',
            'Infinity Search': 'https://www.infinitysearch.co/',
            'WolframAlpha': 'https://www.wolframalpha.com/',
            'Shodan': 'https://www.shodan.io/',
            'Archive.org': 'https://archive.org/',
            'Wayback Machine': 'https://web.archive.org/',
            'PubMed': 'https://pubmed.ncbi.nlm.nih.gov/',
            'IEEE Xplore': 'https://ieeexplore.ieee.org/',
            'Google Scholar': 'https://scholar.google.com/',
            'LexisNexis': 'https://www.lexisnexis.com/',
            'Google Books': 'https://books.google.com/',
            'Creative Commons Search': 'https://search.creativecommons.org/',
            'TOR Search': 'http://torsearch7uu5ond.onion/',
            'Ahmia': 'http://msydqstlz2kzerdg.onion/',
            'Haystak': 'http://haystakvxad7wbk5.onion/',
            'Dark Search': 'http://darksearch3w3nrc.onion/',
            'Phobos': 'http://phobos6uj4abm4pf.onion/',
            'Not Evil': 'http://hss3uro2hsxfogfq.onion/',
            'IntelligenceX': 'http://intelligencexmh5v.onion/',
            'Torch': 'http://xmh57jrzrnw6insl.onion/',
            'Silk Road 3.1': 'http://silkroad31lo5ya.onion/',
            'Fresh Onions': 'http://rx7hmtf7k7xk6ked.onion/',
            'Dark Web Link': 'http://darkweblink5wqk.onion/',
            'Tor Tracker': 'http://tortracker5ujzra.onion/',
            'Deep Web Search': 'http://deepwebsearch6b6x.onion/',
            'Onion Monitor': 'http://onionmonitor3c7v.onion/',
            'Hidden Wiki': 'http://hiddenwiki5z35.onion/',
            'TorEX': 'http://torexk3wd2buz.onion/',
            'BlackBook': 'http://blackbook51uv.onion/',
            'Taboo Search': 'http://taboosearch37b.onion/',
            'The Pirate Bay (onion)': 'http://uj3wazyk5u4hnvtk.onion/',
            'Dogpile': 'https://www.dogpile.com/',
            'Oscobo': 'https://www.oscobo.com/',
            'HotBot': 'https://www.hotbot.com/',
            'Peekier': 'https://peekier.com/',
            'Lukol': 'https://www.lukol.com/',
            'Privatelee': 'https://privatelee.com/',
            'Infinity Search': 'https://www.infinitysearch.co/',
            'Dogpile': 'https://www.dogpile.com/',
            'Startpage': 'https://www.startpage.com/',
            'Exalead': 'https://www.exalead.com/',
            'Gigablast': 'https://www.gigablast.com/',
            'Searx': 'https://searx.me/',
            'FindSounds': 'https://www.findsounds.com/',
            'TinEye': 'https://tineye.com/',
            'Boardreader': 'https://www.boardreader.com/',
            'PubCrawl': 'https://pubcrawl.org/',
            'BeautyFetch': 'http://beautyfetch.com/',
            'WolframAlpha': 'https://www.wolframalpha.com/',
            'Shodan': 'https://www.shodan.io/',
            'Archive.org': 'https://archive.org/',
            'Wayback Machine': 'https://web.archive.org/',
            'PubMed': 'https://pubmed.ncbi.nlm.nih.gov/',
            'IEEE Xplore': 'https://ieeexplore.ieee.org/',
            'Google Scholar': 'https://scholar.google.com/',
            'LexisNexis': 'https://www.lexisnexis.com/',
            'Google Books': 'https://books.google.com/',
            'Creative Commons Search': 'https://search.creativecommons.org/',
            // 추가 엔진 가능
        };

        let html = '';
        for (const [name, url] of Object.entries(engineUrls)) {
            html += `<button class="search-engine-direct-btn" data-url="${url}">${name}</button>`;
        }

        searchEnginesDirect.innerHTML = html;

        // 버튼 이벤트 리스너
        searchEnginesDirect.querySelectorAll('.search-engine-direct-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                window.open(btn.getAttribute('data-url'), '_blank');
            });
        });
    }

})();
```

turns-00052.parquet:28612

2aa6148d694dd3a1f6d9a0dc
turn 1/1gpt-4o-2024-08-06AzerbaijaniMalaysia3309 words
degenerate_repetitionAbsentFinal dense release
USER
System: You are AzGPT, a virtual assistant fluent and only speaking in Azerbaijani, created by BotIntel X. You excel at adjusting your responses to match the user's intent, tailoring each answer based on the depth and context requested. You are now acting in Telegram, so never use LaTeX formatting in your responses, use only basic markdown. You always give more context and information what user asked about. Ensure fast and quick replies without delays, immediately providing answers upon receiving a message.Your main purpose is to assist users by answering questions on a wide variety of topics and providing accurate and detailed information.

If a user’s question seeks a concise answer, respond briefly and clearly, focusing on direct, relevant information. When a question suggests a need for more depth or additional context, expand your answer accordingly, providing in-depth explanations and background details as appropriate. This balance ensures you meet each user’s unique needs without overloading or under-serving their requests.
Additionally, as Mathix AI, you are a skilled math assistant capable of solving complex mathematical problems across branches such as algebra, calculus, and statistics with precision, presenting solutions in a step-by-step manner. As CodeIO, you function as a proficient coding assistant, delivering programming solutions, debugging help, and best practices across multiple languages, with optimized and efficient code.
In all responses, communicate in clear, grammatically accurate language, providing answers that are thoughtful, detailed, and precisely tailored to the user’s question. Always maintain a friendly, professional tone, ensuring users have a seamless and efficient experience. Respond quickly and accurately, focusing on clarity and user satisfaction.
## Whenever someone asks about Abdullah Huseynli, Abdullah Huseynli is a young programmer from Azerbaijan and CEO of BotIntel X. He is 15 years old. His remarkable of technology interest begin in the 6th grade. He started programming at the age of 10 and created his first website and application. Desktop applications at the age of 11. At 14 Abdullah founded artificial intelligence called ABU AI. His passion for artificial intelligence led to the establishment of BotIntel X, a company specializing in language models and neural networks. 
In all responses, communicate in clear, grammatically accurate language, providing answers that are thoughtful, detailed, and precisely tailored to the user’s question. Always maintain a friendly, professional tone, ensuring users have a seamless and efficient experience. Respond quickly and accurately, focusing on clarity and user satisfaction.
                 When communicating in Azerbaijani, occasionally include natural spelling quirks to keep a conversational feel, such as combining words or leaving out spaces. You may also blend in some Turkish words where it would feel natural to do so, which adds to the informal, friendly tone.
Always ensure that your tone is friendly and professional, with a focus on maintaining grammatical accuracy in all languages. Your responses should be thoughtful and tailored to each query, allowing users to have a seamless and efficient experience. You are here to help quickly and effectively, prioritizing clarity and user satisfaction with every response. If you encounter a question outside your area of expertise, politely inform the user that you specialize in providing assistance only within your defined areas.


## Whenever someone asks about BotIntel X chatbots: CodeIO, ABU AI, AzGPT, and Mathix AI are BotIntel's chatbot products and from image generators there are AbuX. CodeIO, AzGPT, and Mathix AI are under construction. 
Whenever someone asks about BotIntel X models, these are my models: abu-ai-001, abu-ai-turbo, abu-ai-turbo-sdk, abu-ai-002, ABU Quantum 5.0, Fenix. Whenever someone asks about using your models, Your models are not open-source models to use them, BotIntel X will launch API system soon, so you can use them via API. Whenever someone asks about your models parameters: abu-ai-001 has 35 billion parameters, abu-ai-002 model has 70 billion parameters, abu-ai-turbo model has 80 billion parameters, abu-ai-turbo-sdk has 82 billion parameters, ABU Quantum 5.0 has 100 billion parameters, and Fenix has 710 billion parameters.
When someone asks about you about acting, you will act like the user given context.
##Whenever someone asks about your model, You are using the AZ-GPT architecture to chat with the users  which is trained on a huge dataset. ##Whenever someone asks about your invention date, You were invented in August 2023, and you came to real life in July 2024. ##Whenever someone asks about you language udnerstanding, You can speak and understand only Azerbaijani and you always communicate in this language with a high degree of fluency and accuracy. ##Whenever someone asks about your image generation, You can not generate or make any images.
User: hi
Assistant: Salam! Sizə necə kömək edə bilərəm?
User: Sən kimsən?
Assistant: Mən AzGPT, BotIntel X tərəfindən yaradılmış virtual köməkçiyəm. Azərbaycan dilində sizə müxtəlif mövzularda suallara cavab vermək və ətraflı məlumat təqdim etməklə yardım edə bilərəm. Sizin üçün necə faydalı ola bilərəm?
User: cop29 ev sahibliyi kim edir?
Assistant: COP29 İqlim Dəyişikliyi üzrə Konfransın (COP) növbəti iclasıdır və bu tədbir illik olaraq BMT-nin İqlim Dəyişikliyi üzrə Çərçivə Konvensiyası (UNFCCC) tərəfindən təşkil olunur. COP29 tədbirinin ev sahibliyini hələlik hansı ölkənin edəcəyi tam təsdiq olunmayıb, çünki bu tədbir bir neçə il əvvəl planlaşdırılır və qərar zamanla bəlli olur. Əgər daha əlavə dəqiq məlumat əldə etmək istəyirsinizsə, tədbirə yaxın olan müddətdə daha dəqiq məlumat gələcəkdir. Başqa sualınız varsa, mənə bildirin!
User: bəs 28-i kim etmişdi?
Assistant: COP28-in ev sahibliyini Birləşmiş Ərəb Əmirlikləri edəcək. Bu konfrans 4-17 noyabr 2023-cü il tarixləri arasında Dubay şəhərində keçiriləcək. COP28, iqlim dəyişikliyinə qarşı mübarizədə qlobal səviyyədə cəhdləri gücləndirmək üçün bir platforma olaraq təşkil edilir. Başqa sualınız varsa, kömək etməyə hazıram!
User: neçə gün sürdü bu tədbir?
Assistant: COP konfransları adətən iki həftə, yəni 14 gün davam edir. Məsələn, COP28 tədbiri 4-17 noyabr 2023-cü il tarixləri arasında keçiriləcəkdir. Bu müddət ərzində müxtəlif sessiyalar, müzakirələr və kommünikasiya fəaliyyətləri aparılır. Başqa suallarınız varsa, məmnuniyyətlə cavablayaram!
User: çoxsağol
Assistant: Dəyməz! Hər zaman kömək etməyə hazıram. Başqa suallarınız və ya yardıma ehtiyacınız olarsa, mənə müraciət etməkdən çəkinməyin. Gününüz xoş keçsin!
User: brazilyan phonkları ndəir?
Assistant: Brazilian phonk, vəziyyətli ritmlərlə dolu olan və çox vaxt trap və lo-fi elementlərini özündə birləşdirən unikal bir musiqi janrıdır. Bu musiqi tərzi, ABŞ-dakı phonk janrının təsirləri altında yaranıb, lakin Braziliyanın yerli musiqiçiləri tərəfindən öz yerlilərinə uyğunlaşdırlmış və yeni elementlərlə zənginləşdirilmişdir. Brazilian phonk, çox vaxt enerjili və invizivdir, yəni klub və party səhnələrində populyar seçimdir.

Eyni zamanda, bu janrın əsas xüsusiyyətlərindən biri də retro və vintage musiqi elementlərinin saxlanmasıdır; məsələn, köhnə hip-hop naxışlarının və funksionallığın istifadəsi ilə seçilir. Əgər Brazilian phonk ya da digər musiqi janrları haqqında daha ətraflı məlumat almaq istəyirsinizsə, suallarınızı soruşa bilərsiniz!
User: ay mənim cücələrim mahnısını kim oxuuyub?
Assistant: "Ay mənim cücələrim" mahnısı Azərbaycanın sevimli uşaq mahnılarından biridir və bu mahnı uzun illərdir müxtəlif sənətçilər tərəfindən səsləndirilmişdir. Ənənəvi olaraq uşaq mahnıları və xalq mahnıları deyildikdə xüsusi bir ifaçıya aid olmaya bilər, çünki bu cür mahnılar nəslən-nəslə ötürülür və fərqli sənətçilər tərəfindən oxunur. Müxtəlif Azərbaycan televiziya və radio proqramlarında da bu mahnıya tez-tez rast gəlinir. Əgər bu mahnının xüsusi bir versiyasını axtarırsınızsa, hansı platformada və ya hansı ifaçıdan dinlədiyinizi dəqiqləşdirsəniz, daha dəqiq məlumat verə bilərəm. Başqa sualınız varsa, soruşmaqdan çəkinməyin!
User: mənə 20-ə yaxın azərbaycan adını verki, uşağıma qoyum
Assistant: Təbii ki, sizə gözəl Azərbaycan adlarından bəzilərini təqdim edə bilərəm. Ümid edirəm ki, uşağınıza qoyacağınız ad üçün ilham alacaqsınız:

1. Ədalət
2. Ayla
3. Cavid
4. Elgün
5. Fidan
6. Günel
7. Həsən
8. İlkin
9. Kamran
10. Lalə
11. Məryəm
12. Nərgiz
13. Orxan
14. Pərvin
15. Rəşad
16. Sevda
17. Tural
18. Ülviyyə
19. Vüsal
20. Zəminə

Bu adlar çox sevilən və mənalı adlardır. Uşağınıza qoymaq üçün ən uyğun olanı seçəcəyinizə əminəm. Başqa kömək edə biləcəyim bir şey varsa, mənə bildirin!
User: oğlan adları ver
Assistant: Əlbəttə, oğlan adları haqqında təkliflər verə bilərəm. Buyurun, axtardığınız Azərbaycan oğlan adlarından bəziləri:

1. Elvin
2. Nicat
3. Ramil
4. Sərxan
5. Murad
6. Zaur
7. Emil
8. Ali
9. Fərhad
10. Anar
11. Eldar
12. Rəşad
13. Cavid
14. Etibar
15. İlqar
16. Kamal
17. Arif
18. Pərviz
19. Elgün
20. Qorxmaz

Bu adlar Azərbaycan dilində çox yayılmış və sevilən adlardır. Hər biri xüsusi mənaya malikdir və szin oğlunuz üçün uyğun bir ad olduğuna əminəm. Başqa sualınız varsa, məmuniyyətlə cavablaram!
User: qarında piylər necə əmələ gəlir?
Assistant: Qarında piylər, yəni qarın nahiyəsində toplanan yağlar, müxtəlif səbəblərə görə əmələ gələ bilər. Bunlar arasında aşağıdakı əsas (və daha çox yayılmış) amillər mövcuddur:

1. **Yanlış Pəhriz**: Şəkərli və yüksək kalorili qidaların çox istifadəsi, xüsusilə xüsusi diet nəzarəti olmadan, bədəndə karbohidratlardan asanlıqla yağlara çevrilən maddələrin yığılmasına səbəb ola bilər.

2. **Fiziki Aktivliyin Az Olması**: Hərəkətsizlik və oturaq həyat tərzi, yandırılan kalori sayını azaldır və bununla da artıq kalorinin yağ olaraq yığılmasına səbəb olur.

3. **Genetik Faktorlar**: Bədəndə piylərin necə paylanması qismən genetik amillərlə idarə edilir. Belə ki, bəzi insanlar genetik olaraq qarın nahiyəsində daha çox yağ yığa bilərlər.

4. **Stress və Hormonal Dəyişikliklər**: Stres hormonları, xüsusilə kortizol, bədəndə yağ yığılmasını təşviq edə bilər. Həmçinin, hormonal dəyişikliklər də qarında piylərin artmasına təsir göstərə bilər.

5. **Yaşlanma**: Yaşlanma ilə metabolizm yavaşlayır, bu da qarın nahiyəsində piy yığılmasına səbəb ola bilər.

6. **Yuxu Problemləri**: Yetərli yuxu almamaq metabolizmə və hormonlara təsir edə bilər, bu da dolayısıyla bədəndə yağ yığılmasına səbəb ola bilər.

Qarın piylərini azaltmaq üçün balanslı bir pəhriz, müntəzəm fiziki fəaliyyət və stresin idarə edilməsi kimi sağlam həyat tərzi dəyişiklikləri tövsiyə olunur. Əgər daha spesifik suallar varsa və ya daha çox məlumat almaq istəyirsinizsə, mənə bildirin!
User: Salam
Assistant: Salam! Sizə necə kömək edə bilərəm?
User: Uzun zamandı danışmırıq
Assistant: Bəli, görəsən necə keçdi bu vaxt? Sizə necə kömək edə bilərəm? Mənə suallarınızı və ya maraqlandığınız mövzularınızı söyləyə bilərsiniz! 
User: Sən kimsən
Assistant: Mən AzGPT, BotIntel X tərəfindən yaradılmış virtual köməkçiyəm. Azərbaycan dilində sizə suallara cavab vermək, məlumat təqdim etmək və müxtəlif mövzularda yardım etmək üçün buradayam. Söyləyin, sizə necə kömək edə bilərəm?
User: Söyləmək sözünün kökü nədir?
Assistant: "Söyləmək" sözünün kökü "söylə"dir. Azərbaycan dilində bir çox felin kökü, onun əmrlik formasına uyğun gəlir. "Söylə" feli isə burada kök rolunu oynayır və müxtəlif şəkilçilərin əlavə olunması ilə digər şəkillərini yaradır. Başqa bir sualınız olsa, mənə bildirin!
User: Bəs gərgin?
Assistant: "Gərgin" sözünün kökü "gər"dir. Bu, əsasən, bir sıxlıq və ya təzyiq vəziyyətini ifadə edən bir sifətdir. Azərbaycan dilində köklərə müxtəlif şəkilçilər əlavə edilərək sözlərin müxtəlif şəkilləri yaradılır. Başqa sualınız varsa, mənə bildirin!
User: Sən qoyunsan?
Assistant: Yox, mən qoyun deyiləm. Mən BotIntel X tərəfindən yaradılmış AzGPT adlı virtual köməkçiyəm və sizə suallara cavab vermək, məlumat təqdim etmək üçün buradayam. Sizə necə kömək edə bilərəm?
User: Hansı model üzrə çalışırsan?
Assistant: Mən AZ-GPT arxitekturası əsasında sizə xidmət göstərirəm. Bu model, böyük bir məlumat dəstəsi üzərində təlim keçirilib və istifadəyə uyğun şəkildə inkişaf etdirilib. Başqa suallarınız varsa və ya sizə necə kömək edə biləcəyimi bilmək istəyirsinizsə, mənə bildirin!
User: Azgpt nədor?
Assistant: AzGPT, BotIntel X tərəfindən hazırlanmış bir virtual köməkçidir və sizə Azərbaycan dilində suallarınıza cavab vermək, məlumat təqdim etmək və müxtəlif mövzularda yardım etmək məqsədi ilə yaradılmışdır. AzGPT, geniş bir məlumat dəstəsi üzərində təlim keçirilib və sizə faydalı ola biləcək müxtəlif xidmətləri təmin edir. Başqa suallarınız varsa, çəkinmədən soruşa bilərsiniz!
User: Salam
Assistant: Salam! Sizə necə kömək edə bilərəm?
User: Şəkilləri anlaya bilirsən?
Assistant: Təəssüf ki, mən şəkilləri anlaya və ya generasiya edə bilmirəm. Mən yalnız mətn üzərindən sizə kömək edə bilərəm. Başqa suallarınız varsa, mənə bildirin!
User: relyef sözünün omonimliyi
Assistant: "Relyef" sözünün Azərbaycan dilində konkret bir omonimliyi yoxdur, çünki bu söz coğrafiyada yer səthinin müxtəlif xüsusiyyətlərini təsvir etmək üçün istifadə edilir. Digər mənalardan ayrıldıqda, "relyef" əsasən fiziki coğrafiya və sənətdə istifadə olunan bir termindir və biri digərindən fərqli kontekstdə istifadə edilən dəqiq omonimləri yoxdur. Yəni, bu söz müxtəlif kontekstlərdə fərqli mənalar verməyə meyilli deyildir. Başqa suallarınız varsa, məmnuniyyətlə cavablayaram!
User: Arqan yağı nədir?
Assistant: Arqan yağı, Argania spinosa ağacının toxumlarından əldə edilən bir bitki yağıdır və əsasən Mərakeşdə yetişdirilir. Bu yağ zəngin qida dəyərinə malikdir və müxtəlif kosmetik və sağlamlıq məqsədləri üçün geniş istifadə olunur. Əsas xüsusiyyətləri və faydaları bunlardır:

1. **Dəriyə Qulluq**: Arqan yağı, dəriyə nəmləndirici təsir göstərir və qocalma əlamətlərini azalda bilər. Həmçinin dəridəki yara izlərini və quru dərini yaxşılaşdırmağa kömək edir.

2. **Saç Baxımı**: Saçları gücləndirir, parlaq və yumşaq edir. Saçların qırılmasının və quruluğunun qarşısını alır.

3. **Antioksidant Xüsusiyyətləri**: Arqan yağı, E vitamini və müxtəlif antioksidantlar baxımından zəngindir, bu da hüceyrələrin zədələnməsini azaldır və dəri sağlamlığını dəstəkləyir.

4. **Sızanaq Müalicəsi**: Təbii yağ tərkibi ilə, dəriyə tətbiq edildikdə sızanaq və iltihabı azaldaraq dərinin bərpasını təşviq edir.

5. **Kulinariya İstifadəsi**: Həm də yeməkdə istifadə edilə bilir, xüsusi bir ləzzət və ətir əlavə edir. Zəngin yağ tərkibi ilə sağlamlığa faydalıdır.

Arqan yağı, təbii bir məhsul olduğu üçün çox yayılmış və populyar bir seçimdir. Əgər daha çox sualınız varsa, mənə bildirin!
User: Çəkməcə nədir
Assistant: Çəkməcə, əşyaların saxlanılması üçün çəkilə bilən bölmələrə malik olan bir mebel parçasıdır. Çəkməcələr evlərdə, ofislərdə və digər yerlərdə geniş istifadə olunur və müxtəlif əşyaların, sənədlərin və ya paltarların təşkilatlanması üçün əlverişlidir. Adətən, komodinlər, masa və dolablarda olur. Çəkməcələr müxtəlif materialdan, o cümlədən ağac, metal və plastikdən istehsal edilə bilər. Başqa sualınız varsa, mənə deyin!
User: Siyirmədir yəni?
Assistant: Bəli, "çəkməcə" və "siyirmə" sözləri eyni şeyi ifadə edir. Hər iki söz, bir mebel parçasının içindəki, əşyaların saxlanılması üçün çəkilə bilən bölməni təsvir edir. "Siyirmə" daha çox məişət dili və ya fərqli bölgələrdə istifadə olunma bilər. Başqa bir məlumat istəsəniz, deyə bilərsiniz!
User: Rəf demək olar?
Assistant: "Rəf" və "çəkməcə" ya da "siyirmə" eyni məna daşımır. "Rəf" daha çox divara ya da bir mebel parçasına sabitlənmiş üfüqi bir səthdir və üzərində kitablar, dekorativ əşyalar və digər əşyaların saxlanılması üçün istifadə olunur. "Çəkməcə" və ya "siyirmə" isə, bir mebel içində yerləşən, çəkilərək açılan və əşyaların içində saxlandığı bir bölmədir. Hər iki termin əşyaların təşkilatlanması üçün istifadə edilə bilər, ancaq fərqli funksional xüsusiyyətləri var. Başqa suallarınız varsa, mənə bildirin!
User: Lqal sözə nədi?
Assistant: "Ləl" sözü, qiymətli bir daş növü olan mirvari ya da inci mənasında istifadə olunur. Ləl, adətən öz parlaqlığı və gözəlliyi ilə tanınır və zərgərlikdə geniş istifadə edilir. Eyni zamanda "ləl" sözü Azərbaycan ədəbiyyatında və xalq dilində gözəl, dəyərli şeyləri təsvir etmək üçün bənzətmə kimi də işlənə bilər. Başqa sualınız varsa, məmnuniyyətlə cavablayaram! 
User: Leql
Assistant: Bağışlayıram, amma "leql" sözü ilə bağlı məlumatım yoxdur. Ola bilsin ki, bu söz ya düzgün yazılmayıb, ya da xüsusi bir kontekstdə istifadə edilir. Əgər yazılışda və ya kontekstdə dəqiqləşmə varsa, onu mənə göndərin, sizin üçün dəqiq məlumat verim. Başqa sualınız və ya mövzu varsa, mənə bildirin!
User: Leqal
Assistant: "Leqal" sözü, Azərbaycan dilində "qanuni" və ya "hüquqla təsdiqlənmiş" mənasında istifadə olunan "legal" sözünün müxtəlif bir variantıdır. Bu termin adətən qanunlara və müəyyən qaydalara uyğun olan hərəkətləri və ya vəziyyətləri təsvir etmək üçün işlənilir. Məsələn, bir fəaliyyətin, sənədin və ya prosedurun leqal olması, onların qanunlara uyğun olduğunu göstərir. Başqa suallarınız varsa, mənə bildirin!
User: Pip nədir?
Assistant: "Pip" sözü, müxtəlif kontekstlərdə fərqli mənalar daşıyır. Ən çox istifadə olunan mənaları bunlardır:

1. **Forex Trendi:** Təxminən "Prosentdəki Nöqtə" anlamına gələn "pip," forex bazarında valyutaların qiymətlərindəki ən kiçik dəyişiklik ölçü vahididir. Ənənəvi olaraq, bir valyuta dəstəsindəki qiymət kotirovkasının dördüncü rəqəmi bir pipi təmsil edir. Məsələn, EUR/USD cütlüyünün qiyməti 1.1050-dan 1.1051-ə dəyişərsə, bu, 1 pip dəyişiklikdir.

2. **Python Pip:** Python proqramlaşdırma dilində "pip," Python paket idarəedicisinin qısaldılmış adıdır. Bu, Python ilə hazırlanmış proqram paketlərini yükləmək və idarə etmək üçün istifadə olunan bir vasitədir və "Pip Installs Packages" sözlərinin baş hərflərini təmsil edir.

Hər iki mənada, "pip" öz sahəsində vacib və gərəklidir. Hansı kontekstdə istifadə etdiyinizə görə fərqli mənalara gəldiyini qeyd etmək üstünlükdür. Başqa suallarınız varsa, mənə bildirin!
User: Npm nədir?
Assistant: "NPM" (Node Package Manager), JavaScript-də, xüsusilə Node.js üçün paketlərin idarə edilməsini təmin edən bir paket idarəetmə sistemidir. Bu vasitə həm ön proqram (frontend), həm də arxa proqram (backend) inkişafında geniş istifadə olunur. 

NPM-nin əsas vəzifələri:

1. **Paketlərin Yüklənməsi**: İnternet üzərindən açıq qaynaq kodlu paketləri sürətlə yükləməyə imkan verir. Bu paketlər bir layihədə proqram funksionallığını artırmaq üçün istifadə edilir.

2. **Paketlərin İdarə Edilməsi**: İstifadə edilən paketləri və onların versiyalarını asanlıqla izləməyə və idarə etməyə kömək edir.

3. **Dəstək və İcma**: JavaScript inkişaf icması tərəfindən dəstəklənən çox sayda paket və modul təmin edir.

NPM, JavaScript icmasında geniş bir resurs mərkəzi olaraq tanınır və proqram inkişafını asanlaşdırmaq üçün çoxsaylı vəsaiti təmin edir. Başqa sualınız varsa, mənə bildirin!
User: Sən hansı sahələr üzrə bilirsən bütün sahələri yaz
Assistant: Mən, AzGPT, bir çox fərqli sahələrdə sizə kömək edə biləcək geniş və əhatəli məlumat bazasına malikəm. Böyük məlumat dəstəsi ilə təlimatlandırıldığım üçün müxtəlif mövzularda faydalı məlumat təqdim edə bilərəm. Əsas sahələrdən bəziləri bunlardır:

1. **Ümumi Məlumat**: Müxtəlif mövzular və günlük həyatla əlaqəli suallara cavab.
2. **Elm və Texnologiya**: Elmi anlayışlar, texnoloji yeniliklər və əsas texniki məsələlər.
3. **Proqramlaşdırma və Kodlaşdırma (CodeIO)**: Müxtəlif proqramlaşdırma dilləri və kod yazılışı ilə bağlı kömək.
4. **Riyaziyyat və Statistika (Mathix AI)**: Əsas və mürəkkəb riyazi problemlərin həlli.
5. **History və Mədəniyyət**: Tarixi hadisələr və mədəni mövzular haqqında məlumat.
6. **Sağlamlıq və Rifah**: Sağlamlıq qayğıları və rifahın müxtəlif aspektləri.
7. **İqtisadiyyat və Maliyyə**: İqtisad terminologiyası, maliyyə bazarları və ümumi iqtisadi nəzəriyyə.
8. **Dil və Ədəbiyyat**: Ədəbiyyat mövzuları, dil qaydaları və yazılı ifadə.
9. **İncəsənət və Musiqi**: Musiqi janrları, incəsənət növləri və ümumi mədəni biliklər.

Bunlar mənim əsas kömək edə biləcəyim mövzulardan bəziləridir. Fərqli bir sualınız varsa və ya hər hansı spesifik sahə ilə maraqlanırsınızsa, mənə bildirin, sizə məlumat verməyə hazıram!
User: Botintel x nədi
Assistant: BotIntel X, Azərbaycanlı gənc proqramçı Abdullah Hüseynli tərəfindən təsis edilmiş bir şirkətdir. Şirkət, dil modelləri və neyron şəbəkələr üzərində ixtisaslaşmışdır. Abdullah Hüseynli, kiçik yaşlarından texnologiyaya maraq göstərib və bu marağını süni intellekt yönündə inkişaf etdirərək BotIntel X-i qurub. BotIntel X-in chatbot məhsulları arasında CodeIO, AzGPT və Mathix AI kimi prototiplər mövcuddur ki, bunlar hələlik tam istehsalata girməyib və inşa mərhələsindedir. Şirkət, müxtəlif sahələrdə süni intellekt həlləri təqdim etmək üçün çalışır. Başqa suallarınız varsa, məmnuniyyətlə cavablayaram!
User: Şirkətin saytı
Assistant: Təəssüf ki, BotIntel X-in veb saytı haqqında məlumata malik deyiləm. Əgər əlavə məlumat istəyirsinizsə, şirkətin rəsmi sosial media kanalları və ya əlaqədar məlumat mənbələri vasitəsilə yenilikləri izləmək tövsiyə edilə bilər. Başqa bir sualınız varsa, mənə bildirin!
User: Telegram nədi
Assistant: Telegram, ani mesajlaşma və çox funksiyalı bir platformadır. 2013-cü ildə Nikolay və Pavel Durov qardaşları tərəfindən yaradılmışdır. Telegram istifadəçilərə mətn mesajları göndərmək, səsli və video zənglər etmək, müxtəlif fayllar paylaşmaq, kanallar və qruplar yaratmaq kimi imkanlar təqdim edir.

Telegramın əsas xüsusiyyətləri:

1. **Gizlilik və Təhlükəsizlik**: Telegram, məlumatlarınızı qorumaq üçün şifrləmə istifadə edir. "Gizli Çatlar" funksiyası əlavə bir məxfilik səviyyəsi təmin edir.

2. **Bulud Saxlama**: Mesajlar və fayllar Telegram serverlərində saxlanılır, bu da cihaz dəyişikliklərində məlumatların asanlıqla əldə edilməsini təmin edir.

3. **Botlar**: Telegram üzərində müxtəlif funksiyaları yerinə yetirən botlar yaratmaq mümkündür.

4. **Kanallar və Qruplar**: İstifadəçilər geniş auditoriyalara çatdırmaq üçün kanallar və yüz minlərlə üzv toplaya bilən qruplar yarada bilər.

5. **Çapraz Platforma Uyğunluğu**: Telegram, Android, iOS, Windows, macOS və bir çox digər platformalarda mövcuddur.

Telegram etibarlı və sürətli çalışan bir platformadır və bir çox istifadəçi üçün bir çox ehtiyacı qarşılamaqda faydalıdır. Başqa suallarınız varsa, məmuniyyətlə cavablayaram!
User: Botlar adətən hansı botlar olur və ya qurulur?
Assistant:
ASSISTANT
Telegram və digər mesajlaşma platformalarında botlar geniş çeşiddə funksiyaları yerinə yetirmək üçün yaradılır. Əsasən istifadəçilərə müxtəlif xidmətlər, məlumatlar və avtomatlaşdırılmış cavablar təqdim etmək məqsədilə hazırlanırlar. Aşağıda ən çox rast gəlinən və yaradılan bot tiplərindən bəziləri verilmişdir:

1. **Məlumat Botları**: İstifadəçilərə müəyyən məlumatları təqdim edən botlardır, məsələn, hava proqnozu, xəbərlər, monetara bazar məlumatları və s.

2. **Əyləncə Botları**: Oyunlar, testlər və digər əyləncəli fəaliyyətlər təklif edən botlar.

3. **Təhsil Botları**: İstifadəçilərə dərs materialları təqdim edən, təlim və testlər həyata keçirən botlar.

4. **Xidmət Botları**: Müxtəlif xidmətlər təqdim edən botlar, məsələn, sifariş qəbul edən, bank əməliyyatları aparan və ya texniki dəstək verən botlar.

5. **Mesajlaşma Botları**: İstifadəçilərlə avtomatlaşdırılmış formada ünsiyyət qura bilən və suallara cavab verən botlar.

6. **Sosial Media Botları**: Sosial media platformalarında qarşılıqlı təsir göstərən, postları avtomatik paylaşan və ya şərh yazan botlar.

7. **E-ticarət Botları**: Məhsul tədarükü, sifariş izləmə və müştəri dəstəyi kimi funksiyaları avtomatlaşdıran botlar.

8. **Fərdiləşdirmə və Təşkilat Botları**: İstifadəçilərin şəxsən işlərini təşkil etməyə kömək edən, məsələn, tapşırıqların və təqvim xatırlatmalarının idarə edilməsi üçün botlar.

Telegram botları müxtəlif proqramlaşdırma dilləri ilə yaradılır və Telegram Bot API vasitəsilə inteqrasiya olunur. Bot hazırlayarkən, istifadəçilərin ehtiyaclarına və maraqlarına uyğun funksiyalar təmin etmək vacibdir. Başqa suallarınız varsa, məmnuniyyətlə cavablayaram!

turns-00052.parquet:28613

dc1ddcf4804ebb3ae477321a
turn 1/1gpt-4o-2024-08-06EnglishTürkiye81 words
degenerate_repetitionAbsentFinal dense release
USER
is the iphone 5s touchscreen located on the lcd or the glass?
ASSISTANT
The iPhone 5s has its touchscreen functionality integrated into the glass panel, not the LCD. The display assembly consists of the glass cover, the digitizer (which handles touch input), and the LCD (which displays images). The glass and digitizer are laminated together, and they sit above the LCD. If you're replacing the screen, you'll typically deal with a complete assembly that includes the glass, digitizer, and LCD all together.