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-00079.parquet:38289

63650dd97169c16fe03e45d1
turn 1/1gpt-4.1-mini-2025-04-14EnglishUnited States23 words
degenerate_repetitionAbsentFinal dense release
USER
User: hi, how are you?
User: hi, how are you?
Assistant:
ASSISTANT
Hello! I'm doing well, thank you. How can I assist you today?

turns-00079.parquet:38290

6017c85c3a25e5a50ebeee88
turn 1/1gpt-4.1-mini-2025-04-14IndonesianChina108 words
degenerate_repetitionAbsentFinal dense release
USER
User: acara jumpa fans dimana saja
User: acara jumpa fans dimana saja
Assistant:
ASSISTANT
Acara jumpa fans biasanya diadakan di berbagai tempat, antara lain:

1. Mall atau pusat perbelanjaan
2. Aula atau gedung serbaguna
3. Kafe atau restoran yang memiliki area khusus
4. Studio televisi atau radio saat acara siaran langsung
5. Tempat konser atau festival musik
6. Event khusus seperti peluncuran album, film, atau produk
7. Convention center atau pameran

Jika kamu ingin tahu jadwal atau lokasi jumpa fans dari artis tertentu, biasanya informasi tersebut diumumkan di media sosial resmi artis atau melalui situs resmi penyelenggara acara. Apakah kamu mencari informasi jumpa fans dari artis atau grup tertentu?

turns-00079.parquet:38291

f31aa9f06cb152d5b35dddde
turn 1/1gpt-4.1-mini-2025-04-14IndonesianChina2779 words
degenerate_repetitionAbsentFinal dense release
USER
User: Setiap suara di alam semesta terdengar oleh Allah SWT. Pendengaran-Nya tidak terbatas, tidak ada satu suarapun yang luput dari-Nya. Dalam hal ini Allah SWT memiliki sifat……...
*
2 points
Al-Sami
Al-Basir
Al-Alim
Al-Khabir
2. Untuk membuktikan kebenaran Allah SWt. Dapat dipergunakan dalil naqli dan dalil aqli. Dalil naqli adalah dalil yang berdasarkan……..
*
2 points
Pemikiran yang matang
Pendapat cendikiawan
Al-Qur’an dan hadist
Hukum yang berlaku
3. Orang yang melaksanakan sholat semata-mata karena Allah Swt, dan berkeinginan hanya untuk menggapai keridhoannnya. Merupakan perwujudan dari salah satu sasaran tujuan melaksanakan salat yaitu……..

*
2 points
Timbulnya keinkhlasan
Timbulnya ketaqwaan kepada Allah Swt
Adanya perasaan nyaman dan tentram
Timbulnya kekacauan
4. Berikut ini hikmah salat fardu dalam menjadi  kehidupan, kecuali………
*
2 points
Membiasakan disiplin
Membiasakan hidup bersosial
Mendapatkan kebahagiaan dunia
Saran pembentukan kepribadian muslim
5. Ketika sedang melakukan salat magrib ransi ragu terhadapa jumlah rakaatnya, sehingga sebelum salam ia melakukan sujud……
*
2 points
Tilawah
Syukur
Sujud rukun
Sahwi
6. Pada saat menerima pengumuman hasil ujian seorang siswa SMP ternyata memperoleh nilai yang memuaskan. Sebagai seorang seorang muslim yang baik, disunahkan untuk mengerjakan sujud…….
*
2 points
Syukur
Tilawah
Sahwi
Rukun
7. Angga mengerjakan soal ujian tanpa menyontek, walaupun banyak kesempatan dan tidak ada pengawas di ruangan. Sikap tersebut menunjukkan bahwa ia beriman kepada malaikat……..
*
2 points
Jibril
Mikail
Rakib dan Atid
Israfil
8. Kita harus sesalu melaksanakan semua perintah Allah Swt. Dan menjauhi segala larangan-nya agar di akhirat dapat balasan surganya Allah Swt. Hal ini cerminan dari iman kepada malaikat………..
*
2 points
Ridwan
Jibril
MIkail
Israfil
9. Membicaarakan Aib yang ada pada diri seseorang dengan maksud untuk merendahkan harga dirinya dinamakan…….
*
2 points
Hasud
Gibah
Dendam
Buruk Sangka
10. Ketika mendengar berita dari media sosial yang merugikan kita, sikap kita adalah………..
*
2 points
Menerima berita dengan senang hati
Mencari sumber berita tersebut
Marah dengan yang memberikan berita
Mengklarifikasi berita tersebut terlebih dahulu
11.  Salah satu sifat terpuji Nabi Muhammad SAW adalah tabligh. Arti dari tabligh adalah...
*
2 points
Jujur
dapat di percaya
Menyampaikan
cerdas
12.  Perintah untuk menuntut ilmu tercantum dalam Al-Qur’an surat...
*
2 points
Al-Falaq
Al-‘Alaq
Al-Kautsar
Al-Ikhlas
13.  Tujuan utama Allah SWT menurunkan rasul kepada umat manusia adalah...
*
2 points
Menjadi pemimpin negara
Menjelaskan hukum-hukum dunia
Memberi kabar gembira dan peringatan
Mengetahui sejarah dunia
14.  Iman kepada rasul termasuk rukun iman yang ke...
*
2 points
1
2
3
4
15.  Rasul yang pertama kali menerima wahyu berupa kitab adalah...
*
2 points
Nabi Musa AS
Nabi Isa AS
Nabi Muhammad SAW
Nabi Ibrahim AS
16.  Perilaku yang mencerminkan iman kepada rasul adalah...
*
2 points
suka mencontek saat ujian
Meneladani akhlak Rasulullah SAW
Malas beribadah
Memusuhi teman yang berbeda agama
17.  Nabi yang mendapat gelar Khalilullah adalah...
*
2 points
Nabi Nuh AS
Nabi Isa AS
Nabi Ibrahim AS
Nabi Muhammad SAW
18.  Nabi Muhammad SAW diangkat menjadi rasul pada usia...
*
2 points
25 tahun
30 tahun
35 tahun
40 tahun
19.   Berikut ini adalah contoh sikap toleransi dalam kehidupan sehari-hari, kecuali...
*
2 points
Menghargai teman yang berbeda agama saat mereka beribadah
Menyela ibadah teman karena ingin bermain
Mengucapkan selamat kepada teman saat hari rayanya
Menghormati perbedaan pendapat dalam diskusi
20.  Toleransi dalam Islam berarti...
*
2 points
Menyamakan semua ajaran agama
Membiarkan orang lain melakukan kesalahan
Menghormati perbedaan tanpa meninggalkan keyakinan sendiri
Mengikuti semua tradisi yang berbeda
21.  Bacaan nun mati atau tanwin yang dibaca jelas dan tidak berdengung disebut...
*
2 points
Idgham
idzhar
ikhfa
iklab
22.  Contoh bacaan ikhfa adalah...
*
2 points
مِنْ رَبِّهِمْ
مَنْ يَقُولُ
أَنْعَمْتَ
مِنْ بَعْدِ
23.  Hukum bacaan nun mati apabila bertemu huruf-huruf idgham adalah...
*
2 points
Dibaca jelas
Dibaca samar
Dimasukkan
Dibalik
24.  Di bawah ini merupakan huruf-huruf idgham bighunnah, kecuali...
*
2 points
ي
م
ن
و
25.  Hukum bacaan nun mati pada lafal "مِنْ بَعْدِ" adalah...
*
2 points
iklab
ikhfa
idghom
idzhar
26.  Salah satu manfaat menuntut ilmu menurut ajaran Islam adalah...
*
2 points
Memperoleh kekayaan secara cepat
Meninggikan derajat seseorang di sisi Allah
Menjadi terkenal di masyarakat
Bisa membantah orang lain
27.  Ayat pertama yang turun kepada Nabi Muhammad SAW berisi perintah untuk...
*
2 points
bergadang
sholat
beribadah
membaca
28.  Salah satu tanda orang yang meyakini hari akhir adalah...
*
2 points
Selalu mengejar kesenangan dunia
Menjaga amal dan menjauhi maksiat
Tidak peduli pada ibadah
Menunda-nunda kebaikan
29.  Hari akhir disebut juga dengan hari pembalasan karena...
*
2 points
Semua orang akan kembali muda
Semua amalan manusia akan dibalas
Dunia akan menjadi lebih indah
Waktu akan berhenti berjalan
30.  Meyakini datangnya hari akhir dapat menumbuhkan sikap...
*
2 points
Serakah dalam mencari harta
Putus asa terhadap masa depan
Tanggung jawab dan mawas diri
Takut bersaing dengan orang lain
31.  Salah satu adab berbicara dalam Islam adalah...
*
2 points
Menaikkan suara agar didengar
Menghina lawan bicara jika salah
Berkata benar dan tidak menyakiti
Menyela pembicaraan orang lain
32.  Etika pergaulan terhadap lawan jenis yang benar menurut Islam adalah...
*
2 points
Bebas bergaul asal tidak berduaan
Menjaga pandangan dan sikap sopan
Saling bersentuhan jika sudah akrab
Tidak perlu membatasi hubungan
33.  Firman Allah dalam QS. Al-Hujurat ayat 11 melarang kita untuk...
*
2 points
Berpuasa tanpa sahur
Berdebat dengan keras
Mengolok-olok dan mencela orang lain
Bertanya tentang hal ghaib
34.  Dampak dari komunikasi yang baik dalam pergaulan adalah...
2 points
Mudah dipengaruhi pergaulan bebas
Dijauhi karena terlalu serius
Terjalinnya hubungan harmonis dan damai
Sering terjadi konflik dan salah paham
35.  Mengapa kita dianjurkan untuk melaksanakan akikah setelah kelahiran anak......
*
2 points
Sebagai bentuk pesta untuk keluarga
Agar anak cepat tumbuh besar
Sebagai bentuk rasa syukur kepada Allah
Untuk menunjukkan kekayaan orang tua
36.  Apa persamaan dari akikah dan kurban?
*
2 points
Dilakukan di bulan Ramadan
Wujud syukur dan saling berbagi kepada sesama
Sebagai bentuk pengorbanan pribadi
Keduanya hanya wajib bagi laki-laki
37.  Hewan yang disembelih untuk aqiqah adalah......
2 points
Kambing atau domba
sapi dan unta
ayam dan kambing
sapi dan kerbau
38.  Berapa jumlah kambing yang disunnahkan untuk anak laki-laki dalam aqiqah.....
*
2 points
1 ekor
2 ekor
3 ekor
4 ekor
39.  Kapan waktu yang paling utama untuk melaksanakan aqiqah..........
*
2 points
hari ke 6
hari ke 7
hari ke 8
hari ke 9
40.  Salah satu sunnah ketika melakukan aqiqah adalah........
*
2 points
Memandikan anak dengan air zam-zam
Memberi nama anak dan mencukur rambutnya
Mengundang semua warga desa
Memberikan susu kepada anak yatim
41.  Apa yang menjadi ciri utama sistem pemerintahan Daulah Utsmaniyah.......
*
2 points
Monarki absolut
Demokrasi langsung
Khilafah Islamiyah
Republik parlementer
42.  Siapakah pendiri Daulah Utsmaniyah........
*
2 points
Sultan Mehmed II
Osman I
Selim I
Murad II
43.  Kota manakah yang ditaklukkan oleh Sultan Mehmed II dan dijadikan ibu kota Daulah Utsmaniyah.....
*
2 points
Damaskus
Konstatinopel
Baghdad
Kairo
44.  Arti dari qada secara bahasa adalah…
*
2 points
Ukuran
ketentuan
balasan
takdir
45.  Meyakini bahwa segala sesuatu yang terjadi di dunia ini telah ditetapkan oleh Allah SWT termasuk dalam…
*
2 points
Rukun Islam
rukun iman
Hukum syariat
ilmu kalam
46.  Ciri utama seni dalam Islam adalah…
*
2 points
Bebas tanpa batas
Meniru seni Barat
Menjaga nilai-nilai tauhid dan etika Islam
Mengagungkan manusia
47.  Gaya arsitektur masjid yang khas dalam seni Islam ditandai dengan…
*
2 points
Patung-patung
salib
Kubah dan menara
Lukisan makhluk hidup
48.  Mazhab yang dikenal paling luas diikuti di wilayah Asia Tenggara adalah...
*
2 points
Hanafi
maliki
Syafi’i
hambali
49.  Mazhab Hanafi banyak dianut di wilayah...
*
2 points
Afrika Utara
India, Pakistan, Turki
Indonesia dan Malaysia
Arab Saudi
50.  Manakah dari imam berikut yang hidup paling awal..........
*
2 points
Imam Abu Hanifah
Imam Syafi’i
Imam Malik
Imam Ahmad bin Hanbal
User: Setiap suara di alam semesta terdengar oleh Allah SWT. Pendengaran-Nya tidak terbatas, tidak ada satu suarapun yang luput dari-Nya. Dalam hal ini Allah SWT memiliki sifat……...
*
2 points
Al-Sami
Al-Basir
Al-Alim
Al-Khabir
2. Untuk membuktikan kebenaran Allah SWt. Dapat dipergunakan dalil naqli dan dalil aqli. Dalil naqli adalah dalil yang berdasarkan……..
*
2 points
Pemikiran yang matang
Pendapat cendikiawan
Al-Qur’an dan hadist
Hukum yang berlaku
3. Orang yang melaksanakan sholat semata-mata karena Allah Swt, dan berkeinginan hanya untuk menggapai keridhoannnya. Merupakan perwujudan dari salah satu sasaran tujuan melaksanakan salat yaitu……..

*
2 points
Timbulnya keinkhlasan
Timbulnya ketaqwaan kepada Allah Swt
Adanya perasaan nyaman dan tentram
Timbulnya kekacauan
4. Berikut ini hikmah salat fardu dalam menjadi  kehidupan, kecuali………
*
2 points
Membiasakan disiplin
Membiasakan hidup bersosial
Mendapatkan kebahagiaan dunia
Saran pembentukan kepribadian muslim
5. Ketika sedang melakukan salat magrib ransi ragu terhadapa jumlah rakaatnya, sehingga sebelum salam ia melakukan sujud……
*
2 points
Tilawah
Syukur
Sujud rukun
Sahwi
6. Pada saat menerima pengumuman hasil ujian seorang siswa SMP ternyata memperoleh nilai yang memuaskan. Sebagai seorang seorang muslim yang baik, disunahkan untuk mengerjakan sujud…….
*
2 points
Syukur
Tilawah
Sahwi
Rukun
7. Angga mengerjakan soal ujian tanpa menyontek, walaupun banyak kesempatan dan tidak ada pengawas di ruangan. Sikap tersebut menunjukkan bahwa ia beriman kepada malaikat……..
*
2 points
Jibril
Mikail
Rakib dan Atid
Israfil
8. Kita harus sesalu melaksanakan semua perintah Allah Swt. Dan menjauhi segala larangan-nya agar di akhirat dapat balasan surganya Allah Swt. Hal ini cerminan dari iman kepada malaikat………..
*
2 points
Ridwan
Jibril
MIkail
Israfil
9. Membicaarakan Aib yang ada pada diri seseorang dengan maksud untuk merendahkan harga dirinya dinamakan…….
*
2 points
Hasud
Gibah
Dendam
Buruk Sangka
10. Ketika mendengar berita dari media sosial yang merugikan kita, sikap kita adalah………..
*
2 points
Menerima berita dengan senang hati
Mencari sumber berita tersebut
Marah dengan yang memberikan berita
Mengklarifikasi berita tersebut terlebih dahulu
11.  Salah satu sifat terpuji Nabi Muhammad SAW adalah tabligh. Arti dari tabligh adalah...
*
2 points
Jujur
dapat di percaya
Menyampaikan
cerdas
12.  Perintah untuk menuntut ilmu tercantum dalam Al-Qur’an surat...
*
2 points
Al-Falaq
Al-‘Alaq
Al-Kautsar
Al-Ikhlas
13.  Tujuan utama Allah SWT menurunkan rasul kepada umat manusia adalah...
*
2 points
Menjadi pemimpin negara
Menjelaskan hukum-hukum dunia
Memberi kabar gembira dan peringatan
Mengetahui sejarah dunia
14.  Iman kepada rasul termasuk rukun iman yang ke...
*
2 points
1
2
3
4
15.  Rasul yang pertama kali menerima wahyu berupa kitab adalah...
*
2 points
Nabi Musa AS
Nabi Isa AS
Nabi Muhammad SAW
Nabi Ibrahim AS
16.  Perilaku yang mencerminkan iman kepada rasul adalah...
*
2 points
suka mencontek saat ujian
Meneladani akhlak Rasulullah SAW
Malas beribadah
Memusuhi teman yang berbeda agama
17.  Nabi yang mendapat gelar Khalilullah adalah...
*
2 points
Nabi Nuh AS
Nabi Isa AS
Nabi Ibrahim AS
Nabi Muhammad SAW
18.  Nabi Muhammad SAW diangkat menjadi rasul pada usia...
*
2 points
25 tahun
30 tahun
35 tahun
40 tahun
19.   Berikut ini adalah contoh sikap toleransi dalam kehidupan sehari-hari, kecuali...
*
2 points
Menghargai teman yang berbeda agama saat mereka beribadah
Menyela ibadah teman karena ingin bermain
Mengucapkan selamat kepada teman saat hari rayanya
Menghormati perbedaan pendapat dalam diskusi
20.  Toleransi dalam Islam berarti...
*
2 points
Menyamakan semua ajaran agama
Membiarkan orang lain melakukan kesalahan
Menghormati perbedaan tanpa meninggalkan keyakinan sendiri
Mengikuti semua tradisi yang berbeda
21.  Bacaan nun mati atau tanwin yang dibaca jelas dan tidak berdengung disebut...
*
2 points
Idgham
idzhar
ikhfa
iklab
22.  Contoh bacaan ikhfa adalah...
*
2 points
مِنْ رَبِّهِمْ
مَنْ يَقُولُ
أَنْعَمْتَ
مِنْ بَعْدِ
23.  Hukum bacaan nun mati apabila bertemu huruf-huruf idgham adalah...
*
2 points
Dibaca jelas
Dibaca samar
Dimasukkan
Dibalik
24.  Di bawah ini merupakan huruf-huruf idgham bighunnah, kecuali...
*
2 points
ي
م
ن
و
25.  Hukum bacaan nun mati pada lafal "مِنْ بَعْدِ" adalah...
*
2 points
iklab
ikhfa
idghom
idzhar
26.  Salah satu manfaat menuntut ilmu menurut ajaran Islam adalah...
*
2 points
Memperoleh kekayaan secara cepat
Meninggikan derajat seseorang di sisi Allah
Menjadi terkenal di masyarakat
Bisa membantah orang lain
27.  Ayat pertama yang turun kepada Nabi Muhammad SAW berisi perintah untuk...
*
2 points
bergadang
sholat
beribadah
membaca
28.  Salah satu tanda orang yang meyakini hari akhir adalah...
*
2 points
Selalu mengejar kesenangan dunia
Menjaga amal dan menjauhi maksiat
Tidak peduli pada ibadah
Menunda-nunda kebaikan
29.  Hari akhir disebut juga dengan hari pembalasan karena...
*
2 points
Semua orang akan kembali muda
Semua amalan manusia akan dibalas
Dunia akan menjadi lebih indah
Waktu akan berhenti berjalan
30.  Meyakini datangnya hari akhir dapat menumbuhkan sikap...
*
2 points
Serakah dalam mencari harta
Putus asa terhadap masa depan
Tanggung jawab dan mawas diri
Takut bersaing dengan orang lain
31.  Salah satu adab berbicara dalam Islam adalah...
*
2 points
Menaikkan suara agar didengar
Menghina lawan bicara jika salah
Berkata benar dan tidak menyakiti
Menyela pembicaraan orang lain
32.  Etika pergaulan terhadap lawan jenis yang benar menurut Islam adalah...
*
2 points
Bebas bergaul asal tidak berduaan
Menjaga pandangan dan sikap sopan
Saling bersentuhan jika sudah akrab
Tidak perlu membatasi hubungan
33.  Firman Allah dalam QS. Al-Hujurat ayat 11 melarang kita untuk...
*
2 points
Berpuasa tanpa sahur
Berdebat dengan keras
Mengolok-olok dan mencela orang lain
Bertanya tentang hal ghaib
34.  Dampak dari komunikasi yang baik dalam pergaulan adalah...
2 points
Mudah dipengaruhi pergaulan bebas
Dijauhi karena terlalu serius
Terjalinnya hubungan harmonis dan damai
Sering terjadi konflik dan salah paham
35.  Mengapa kita dianjurkan untuk melaksanakan akikah setelah kelahiran anak......
*
2 points
Sebagai bentuk pesta untuk keluarga
Agar anak cepat tumbuh besar
Sebagai bentuk rasa syukur kepada Allah
Untuk menunjukkan kekayaan orang tua
36.  Apa persamaan dari akikah dan kurban?
*
2 points
Dilakukan di bulan Ramadan
Wujud syukur dan saling berbagi kepada sesama
Sebagai bentuk pengorbanan pribadi
Keduanya hanya wajib bagi laki-laki
37.  Hewan yang disembelih untuk aqiqah adalah......
2 points
Kambing atau domba
sapi dan unta
ayam dan kambing
sapi dan kerbau
38.  Berapa jumlah kambing yang disunnahkan untuk anak laki-laki dalam aqiqah.....
*
2 points
1 ekor
2 ekor
3 ekor
4 ekor
39.  Kapan waktu yang paling utama untuk melaksanakan aqiqah..........
*
2 points
hari ke 6
hari ke 7
hari ke 8
hari ke 9
40.  Salah satu sunnah ketika melakukan aqiqah adalah........
*
2 points
Memandikan anak dengan air zam-zam
Memberi nama anak dan mencukur rambutnya
Mengundang semua warga desa
Memberikan susu kepada anak yatim
41.  Apa yang menjadi ciri utama sistem pemerintahan Daulah Utsmaniyah.......
*
2 points
Monarki absolut
Demokrasi langsung
Khilafah Islamiyah
Republik parlementer
42.  Siapakah pendiri Daulah Utsmaniyah........
*
2 points
Sultan Mehmed II
Osman I
Selim I
Murad II
43.  Kota manakah yang ditaklukkan oleh Sultan Mehmed II dan dijadikan ibu kota Daulah Utsmaniyah.....
*
2 points
Damaskus
Konstatinopel
Baghdad
Kairo
44.  Arti dari qada secara bahasa adalah…
*
2 points
Ukuran
ketentuan
balasan
takdir
45.  Meyakini bahwa segala sesuatu yang terjadi di dunia ini telah ditetapkan oleh Allah SWT termasuk dalam…
*
2 points
Rukun Islam
rukun iman
Hukum syariat
ilmu kalam
46.  Ciri utama seni dalam Islam adalah…
*
2 points
Bebas tanpa batas
Meniru seni Barat
Menjaga nilai-nilai tauhid dan etika Islam
Mengagungkan manusia
47.  Gaya arsitektur masjid yang khas dalam seni Islam ditandai dengan…
*
2 points
Patung-patung
salib
Kubah dan menara
Lukisan makhluk hidup
48.  Mazhab yang dikenal paling luas diikuti di wilayah Asia Tenggara adalah...
*
2 points
Hanafi
maliki
Syafi’i
hambali
49.  Mazhab Hanafi banyak dianut di wilayah...
*
2 points
Afrika Utara
India, Pakistan, Turki
Indonesia dan Malaysia
Arab Saudi
50.  Manakah dari imam berikut yang hidup paling awal..........
*
2 points
Imam Abu Hanifah
Imam Syafi’i
Imam Malik
Imam Ahmad bin Hanbal
Assistant:
ASSISTANT
Berikut adalah jawaban untuk pertanyaan yang Anda berikan:

1. **Al-Sami**  
2. **Al-Qur’an dan hadist**  
3. **Timbulnya keikhlasan**  
4. **Mendapatkan kebahagiaan dunia**  
5. **Sahwi**  
6. **Syukur**  
7. **Rakib dan Atid**  
8. **Ridwan**  
9. **Gibah**  
10. **Mengklarifikasi berita tersebut terlebih dahulu**  
11. **Menyampaikan**  
12. **Al-‘Alaq**  
13. **Memberi kabar gembira dan peringatan**  
14. **2**  
15. **Nabi Musa AS**  
16. **Meneladani akhlak Rasulullah SAW**  
17. **Nabi Ibrahim AS**  
18. **40 tahun**  
19. **Menyela ibadah teman karena ingin bermain**  
20. **Menghormati perbedaan tanpa meninggalkan keyakinan sendiri**  
21. **idzhar**  
22. **مِنْ رَبِّهِمْ**  
23. **Dimasukkan**  
24. **ن**  
25. **ikhfa**  
26. **Meninggikan derajat seseorang di sisi Allah**  
27. **membaca**  
28. **Menjaga amal dan menjauhi maksiat**  
29. **Semua amalan manusia akan dibalas**  
30. **Tanggung jawab dan mawas diri**  
31. **Berkata benar dan tidak menyakiti**  
32. **Menjaga pandangan dan sikap sopan**  
33. **Mengolok-olok dan mencela orang lain**  
34. **Terjalinnya hubungan harmonis dan damai**  
35. **Sebagai bentuk rasa syukur kepada Allah**  
36. **Wujud syukur dan saling berbagi kepada sesama**  
37. **Kambing atau domba**  
38. **2 ekor**  
39. **hari ke 7**  
40. **Memberi nama anak dan mencukur rambutnya**  
41. **Khilafah Islamiyah**  
42. **Osman I**  
43. **Konstatinopel**  
44. **ketentuan**  
45. **rukun iman**  
46. **Menjaga nilai-nilai tauhid dan etika Islam**  
47. **Kubah dan menara**  
48. **Syafi’i**  
49. **India, Pakistan, Turki**  
50. **Imam Abu Hanifah**

Semoga membantu! Jika ada pertanyaan tambahan, silakan ditanyakan.

turns-00079.parquet:38292

767ba7b9ef23b3b5f8e25626
turn 1/1gpt-4.1-mini-2025-04-14SothoMexico7 words
degenerate_repetitionAbsentFinal dense release
USER
hola
ASSISTANT
¡Hola! ¿En qué puedo ayudarte hoy?

turns-00079.parquet:38293

e7a402a58bc2861b77ce9cce
turn 1/4gpt-4.1-mini-2025-04-14ArabicYemen2371 words
degenerate_repetitionAbsentFinal dense release
USER
اريد اضافة باقي الاوقات
import requests
from bs4 import BeautifulSoup
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (ApplicationBuilder, CommandHandler, MessageHandler,
                          ContextTypes, filters, CallbackQueryHandler,
                          ConversationHandler)
import logging
import json
import os
from datetime import datetime, timedelta
import pytz
from tabulate import tabulate

# Flask و threading لخدمة الويب
from flask import Flask
import threading

app = Flask('')

@app.route('/')
def home():
    return "Bot is alive!"

def run():
    app.run(host="0.0.0.0", port=8080)


logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO)
logger = logging.getLogger(__name__)

ADD_NUMBER, DELETE_NUMBER, SCHEDULE_HOUR = range(3)

SAVED_NUMBERS_FILE = "saved_numbers.json"
SCHEDULED_TIMES_FILE = "scheduled_times.json"

YEMEN_TZ = pytz.timezone('Asia/Aden')

# --- التعديل: التحميل وحفظ حسب chat_id ---

def load_saved_numbers():
    if os.path.exists(SAVED_NUMBERS_FILE):
        try:
            with open(SAVED_NUMBERS_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"خطأ في تحميل الأرقام المحفوظة: {e}")
            return {}
    else:
        return {}

def load_scheduled_times():
    if os.path.exists(SCHEDULED_TIMES_FILE):
        try:
            with open(SCHEDULED_TIMES_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"خطأ في تحميل الأوقات المجدولة: {e}")
            return {}
    else:
        return {}

def save_numbers_to_file(numbers):
    try:
        with open(SAVED_NUMBERS_FILE, 'w', encoding='utf-8') as f:
            json.dump(numbers, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"خطأ في حفظ الأرقام المحفوظة: {e}")

def save_scheduled_times(times):
    try:
        with open(SCHEDULED_TIMES_FILE, 'w', encoding='utf-8') as f:
            json.dump(times, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"خطأ في حفظ الأوقات المجدولة: {e}")

saved_numbers_all = load_saved_numbers()
scheduled_times = load_scheduled_times()
scheduled_jobs = {}

ADSL_URL = "https://adsl-yemen.com/adsl4.php"

def get_adsl_info(phone_number):
    try:
        data = {"mobile": phone_number, "action": "query"}
        response = requests.post(ADSL_URL, data=data)

        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            results = soup.find_all("div", class_="result-box")
            data_dict = {}
            for box in results:
                title_div = box.find("div", class_="result-title")
                value_div = box.find("div", class_="result-value")
                if title_div and value_div:
                    title = title_div.text.strip()
                    value = value_div.text.strip()
                    data_dict[title] = value

            data_dict['timestamp'] = datetime.now(YEMEN_TZ).strftime("%Y-%m-%d %H:%M:%S")
            return data_dict
        else:
            return None
    except Exception as e:
        logger.error(f"حدث خطأ أثناء استعلام الرصيد: {e}")
        return None

async def send_balance_info(update, phone_number, info):
    if info:
        response = (
            f"🌟 *نتائج استعلام رصيد ADSL*\n"
            f"══════════════════════════\n"
            f"📱 *الرقم:* `{phone_number}`\n\n"
            f"💳 *الرصيد الحالي:* `{info.get('الرصيد الحالي', 'غير متوفر')}`\n"
            f"📦 *قيمة الباقة:* `{info.get('قيمة الباقة', 'غير متوفر')}`\n"
            f"⏳ *تاريخ الانتهاء:* `{info.get('تاريخ الانتهاء', 'غير متوفر')}`\n\n"
            f"🔄 يمكنك إضافة هذا الرقم إلى المحفوظات للاستعلام عنه لاحقاً")
    else:
        response = ("⚠️ *عذراً، تعذر الحصول على معلومات الرصيد*\n"
                    "══════════════════════════\n"
                    "🛑 *الأسباب المحتملة:*\n"
                    "• الرقم غير مسجل في خدمة ADSL\n"
                    "• وجود مشكلة تقنية مؤقتة\n"
                    "• خطأ في الاتصال بالخادم\n\n"
                    "🔍 يرجى التأكد من الرقم والمحاولة لاحقاً")

    if isinstance(update, Update):
        await update.message.reply_text(response, parse_mode='Markdown')
    else:
        await update.reply_text(response, parse_mode='Markdown')


async def add_number(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    chat_id = str(update.effective_chat.id)
    phone_number = update.message.text.strip()

    if not phone_number.isdigit() or len(phone_number) != 8:
        await update.message.reply_text(
            "⚠️ *رقم غير صحيح!*\nيجب أن يتكون الرقم من 8 أرقام فقط.\nمثال: 71234567",
            parse_mode='Markdown')
        return ADD_NUMBER

    # تحقق وحفظ الرقم للمستخدم فقط
    if chat_id not in saved_numbers_all:
        saved_numbers_all[chat_id] = {}
    saved_numbers_all[chat_id][phone_number] = True
    save_numbers_to_file(saved_numbers_all)

    await update.message.reply_text(f"✅ تم حفظ الرقم {phone_number} بنجاح!",
                                    parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user = update.effective_user

    welcome_message = (
        f"✨ *مرحباً بك {user.first_name} في بوت استعلام رصيد ADSL اليمن* ✨\n\n"
        "🚀 *خدمات البوت المتكاملة:*\n"
        "• 📲 استعلام فوري عن رصيد أي خط ADSL\n"
        "• 💾 إدارة الأرقام المحفوظة بسهولة\n"
        "• ⏰ جدولة استعلامات تلقائية يومية\n"
        "• 📊 عرض التقارير المفصلة\n\n"
        "📌 *كيفية الاستخدام:*\n"
        "• أرسل رقم الهاتف مباشرة للاستعلام الفوري\n"
        "• أو استخدم القائمة أدناه للوصول إلى جميع الميزات")

    keyboard = [[
        InlineKeyboardButton("📲 استعلام فوري", callback_data='new_query'),
        InlineKeyboardButton("💾 إدارة الأرقام", callback_data='manage_numbers')
    ],
                [
                    InlineKeyboardButton("⏰ جدولة تلقائية",
                                         callback_data='schedule_query'),
                    InlineKeyboardButton("📊 الاستعلام الجماعي",
                                         callback_data='query_numbers')
                ], [InlineKeyboardButton("ℹ️ المساعدة", callback_data='help')]]
    reply_markup = InlineKeyboardMarkup(keyboard)

    if update.message:
        await update.message.reply_text(welcome_message,
                                        reply_markup=reply_markup,
                                        parse_mode='Markdown')
    else:
        await update.callback_query.edit_message_text(
            welcome_message, reply_markup=reply_markup, parse_mode='Markdown')


async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    chat_id = str(update.effective_chat.id)
    phone_number = update.message.text.strip()

    if not phone_number.isdigit() or len(phone_number) != 8:
        await update.message.reply_text(
            "⚠️ *يرجى إدخال رقم هاتف صحيح*\n"
            "══════════════════════════\n"
            "📌 *الشروط المطلوبة:*\n"
            "• يجب أن يتكون الرقم من 8 أرقام\n"
            "• يجب أن يحتوي على أرقام فقط\n"
            "• مثال: `71234567`",
            parse_mode='Markdown')
        return

    processing_msg = await update.message.reply_text(
        "⏳ *جاري معالجة طلبك...*\n"
        "══════════════════════════\n"
        "📡 يتم الآن الاتصال بخادم ADSL\n"
        "⏱️ قد يستغرق الأمر بضع ثوانٍ...",
        parse_mode='Markdown')

    info = get_adsl_info(phone_number)

    await processing_msg.delete()
    await send_balance_info(update, phone_number, info)

    # عرض خيار حفظ الرقم للمستخدم فقط
    if info:
        user_numbers = saved_numbers_all.get(chat_id, {})
        if phone_number not in user_numbers:
            keyboard = [[
                InlineKeyboardButton("💾 حفظ الرقم",
                                     callback_data=f'save_{phone_number}'),
                InlineKeyboardButton("🔄 استعلام جديد", callback_data='new_query')
            ], [InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')]]
            reply_markup = InlineKeyboardMarkup(keyboard)
            await update.message.reply_text(
                "❔ *هل ترغب في حفظ هذا الرقم للاستعلام عنه لاحقاً؟*",
                reply_markup=reply_markup,
                parse_mode='Markdown')


async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data == 'add_number':
        await query.edit_message_text(
            "📝 *إضافة رقم جديد للمحفوظات*\n"
            "══════════════════════════\n"
            "📱 يرجى إرسال رقم الهاتف المكون من 8 أرقام\n"
            "✏️ مثال: `71234567`\n\n"
            "🔙 للعودة اضغط /start",
            parse_mode='Markdown')
        return ADD_NUMBER

    elif query.data == 'manage_numbers':
        user_numbers = saved_numbers_all.get(chat_id, {})
        if not user_numbers:
            await query.edit_message_text(
                "⚠️ *لا توجد أرقام محفوظة*\n"
                "══════════════════════════\n"
                "لم تقم بإضافة أي أرقام إلى المحفوظات بعد\n\n"
                "➕ يمكنك إضافة أرقام جديدة من القائمة الرئيسية\n\n"
                "🏠 اضغط للعودة إلى الرئيسية",
                reply_markup=InlineKeyboardMarkup(
                    [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]),
                parse_mode='Markdown')
            return

        keyboard = []
        for num in user_numbers:
            keyboard.append([
                InlineKeyboardButton(f"🗑️ حذف {num}", callback_data=f'delete_{num}')
            ])
        keyboard.append([InlineKeyboardButton("➕ إضافة رقم جديد", callback_data='add_number')])
        keyboard.append([InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')])
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "🗂️ *إدارة الأرقام المحفوظة*\n"
            "══════════════════════════\n"
            "🔘 اختر الرقم الذي تريد إدارته:\n\n"
            "🗑️ زر الحذف - لإزالة الرقم من المحفوظات",
            reply_markup=reply_markup,
            parse_mode='Markdown')
        return DELETE_NUMBER

    elif query.data == 'query_numbers':
        user_numbers = saved_numbers_all.get(chat_id, {})
        if not user_numbers:
            await query.edit_message_text(
                "📊 *الاستعلام الجماعي*\n"
                "══════════════════════════\n"
                "⚠️ لا توجد أرقام محفوظة حالياً\n\n"
                "➕ يمكنك إضافة أرقام جديدة من القائمة الرئيسية\n\n"
                "🏠 اضغط للعودة إلى الرئيسية",
                reply_markup=InlineKeyboardMarkup(
                    [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]),
                parse_mode='Markdown')
            return

        processing_msg = await query.message.reply_text(
            "⏳ *جاري استعلام الأرقام المحفوظة*\n"
            "══════════════════════════\n"
            "📡 يتم الآن جلب أحدث المعلومات عن جميع الأرقام\n"
            "⏱️ قد تستغرق العملية بعض الوقت...",
            parse_mode='Markdown')

        table_data = []
        for num in user_numbers:
            info = get_adsl_info(num)
            if info:
                table_data.append([
                    num,
                    info.get('الرصيد الحالي', 'غير متوفر'),
                    info.get('تاريخ الانتهاء', 'غير متوفر')
                ])
            else:
                table_data.append([num, "❌ فشل", "❌ فشل"])

        headers = ["📱 الرقم", "💵 الرصيد", "📅 الانتهاء"]
        table = tabulate(table_data, headers=headers, tablefmt="grid")

        response = ("📊 *نتائج الاستعلام الجماعي*\n"
                    "══════════════════════════\n"
                    f"```\n{table}\n```\n"
                    f"🔢 *عدد الأرقام:* {len(user_numbers)}\n\n"
                    "✨ تم الانتهاء من عملية الاستعلام بنجاح")

        await processing_msg.delete()
        await query.message.reply_text(response, parse_mode='Markdown')

    elif query.data == 'schedule_query':
        keyboard = [[
            InlineKeyboardButton("🕘 9 صباحاً", callback_data='hour_9_AM'),
            InlineKeyboardButton("🕛 12 ظهراً", callback_data='hour_12_PM')
        ],
                    [
                        InlineKeyboardButton("🕓 4 عصراً", callback_data='hour_4_PM'),
                        InlineKeyboardButton("🕗 8 مساءً", callback_data='hour_8_PM')
                    ],
                    [
                        InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')
                    ]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "⏰ *جدولة الاستعلام التلقائي*\n"
            "══════════════════════════\n"
            "📅 يرجى تحديد الوقت المفضل للاستعلام اليومي\n\n"
            "⏱️ سيتم إجراء الاستعلام تلقائياً في الوقت المحدد\n\n"
            "🏠 يمكنك العودة للمباشرة",
            reply_markup=reply_markup,
            parse_mode='Markdown')
        return SCHEDULE_HOUR

    elif query.data == 'help':
        help_text = ("ℹ️ *دليل استخدام البوت*\n"
                     "══════════════════════════\n"
                     "🔹 *استعلام فوري*:\n"
                     "   - أرسل الرقم مباشرة (8 أرقام)\n"
                     "   - أو اضغط على 'استعلام فوري'\n\n"
                     "🔹 *إدارة الأرقام*:\n"
                     "   - حفظ الأرقام للاستعلام الجماعي\n"
                     "   - حذف الأرقام المحفوظة\n\n"
                     "🔹 *الاستعلام الجماعي*:\n"
                     "   - استعلام جميع الأرقام المحفوظة دفعة واحدة\n\n"
                     "🔹 *جدولة استعلام*:\n"
                     "   - ضبط استعلام تلقائي يومي في وقت محدد\n\n"
                     "📌 *ملاحظات مهمة*:\n"
                     "   - يجب أن يتكون الرقم من 8 أرقام\n"
                     "   - الخدمة متوفرة لجميع عملاء ADSL اليمن\n\n"
                     "🏠 اضغط للعودة إلى الرئيسية")
        keyboard = [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(help_text, reply_markup=reply_markup, parse_mode='Markdown')

    elif query.data == 'back':
        await start(update, context)

    elif query.data == 'new_query':
        keyboard = [[InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "🔍 *استعلام جديد*\n"
            "══════════════════════════\n"
            "📱 يرجى إرسال رقم الهاتف المكون من 8 أرقام\n"
            "✏️ مثال: `71234567`",
            reply_markup=reply_markup,
            parse_mode='Markdown')

    elif query.data.startswith('save_'):
        phone_number = query.data[5:]
        if chat_id not in saved_numbers_all:
            saved_numbers_all[chat_id] = {}
        saved_numbers_all[chat_id][phone_number] = True
        save_numbers_to_file(saved_numbers_all)
        await query.edit_message_text(
            f"✅ *تم الحفظ بنجاح*\n"
            f"══════════════════════════\n"
            f"تم حفظ الرقم `{phone_number}` في المحفوظات\n\n"
            f"📋 يمكنك الآن إجراء استعلام جماعي أو جدولة استعلامات تلقائية",
            parse_mode='Markdown')
        await start(update, context)


async def delete_number(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data.startswith('delete_'):
        num_to_delete = query.data[7:]
        user_numbers = saved_numbers_all.get(chat_id, {})
        if num_to_delete in user_numbers:
            del user_numbers[num_to_delete]
            save_numbers_to_file(saved_numbers_all)
            await query.edit_message_text(
                f"✅ *تم الحذف بنجاح*\n"
                f"══════════════════════════\n"
                f"• *الرقم المحذوف:* `{num_to_delete}`\n\n"
                f"📋 يمكنك إضافة أرقام جديدة من القائمة الرئيسية",
                parse_mode='Markdown')
        else:
            await query.edit_message_text(
                "⚠️ *تعذر الحذف*\n"
                "══════════════════════════\n"
                "• الرقم المطلوب غير موجود في المحفوظات\n\n"
                "📋 يرجى التحقق من الأرقام المحفوظة والمحاولة مرة أخرى",
                parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def schedule_hour(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data.startswith('hour_'):
        time_parts = query.data.split('_')
        hour = int(time_parts[1])
        ampm = time_parts[2]

        # تحويل إلى 24 ساعة
        if ampm == 'PM' and hour < 12:
            hour += 12
        elif ampm == 'AM' and hour == 12:
            hour = 0

        # حفظ الجدولة (وقت واحد فقط لكل مستخدم)
        scheduled_times[chat_id] = {'hour': hour, 'minute': 0, 'ampm': ampm}
        save_scheduled_times(scheduled_times)

        now = datetime.now(YEMEN_TZ)
        scheduled_time = now.replace(hour=hour, minute=0, second=0, microsecond=0)
        if scheduled_time < now:
            scheduled_time += timedelta(days=1)

        # إزالة الجدولة السابقة إن وجدت
        if chat_id in scheduled_jobs:
            scheduled_jobs[chat_id].schedule_removal()

        try:
            job = context.application.job_queue.run_repeating(
                auto_query_callback,
                interval=timedelta(days=1),
                first=timedelta(seconds=(scheduled_time - now).total_seconds()),
                chat_id=int(chat_id))
            scheduled_jobs[chat_id] = job

            await query.edit_message_text(
                f"✅ *تمت الجدولة بنجاح*\n"
                f"══════════════════════════\n"
                f"• *وقت الاستعلام:* {time_parts[1]} {'صباحاً' if ampm == 'AM' else 'مساءً'}\n"
                f"• سيبدأ الاستعلام التلقائي يومياً في الوقت المحدد\n\n"
                f"📋 يمكنك تعديل الإعدادات مرة واحدة يومياً من القائمة الرئيسية",
                parse_mode='Markdown')
        except Exception as e:
            logger.error(f"خطأ في الجدولة: {e}")
            await query.edit_message_text(
                "⚠️ *تعذر إعداد الجدولة*\n"
                "══════════════════════════\n"
                "حدث خطأ تقني أثناء محاولة جدولة الاستعلام\n\n"
                "🔄 يرجى المحاولة مرة أخرى",
                parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def auto_query_callback(context: ContextTypes.DEFAULT_TYPE):
    job = context.job
    chat_id = str(job.chat_id)

    user_numbers = saved_numbers_all.get(chat_id, {})
    if not user_numbers:
        await context.bot.send_message(
            job.chat_id, "⚠️ *لا توجد أرقام محفوظة*\n"
            "══════════════════════════\n"
            "لم يتم العثور على أي أرقام محفوظة للاستعلام عنها\n\n"
            "➕ يرجى إضافة أرقام أولاً من القائمة الرئيسية",
            parse_mode='Markdown')
        return

    processing_msg = await context.bot.send_message(
        job.chat_id, "⏳ *جاري تنفيذ الاستعلام التلقائي*\n"
        "══════════════════════════\n"
        "📡 يتم الآن جلب أحدث المعلومات عن الأرقام المحفوظة\n"
        "⏱️ يرجى الانتظار...",
        parse_mode='Markdown')

    table_data = []
    for num in user_numbers:
        info = get_adsl_info(num)
        if info:
            table_data.append([
                num,
                info.get('الرصيد الحالي', 'غير متوفر'),
                info.get('تاريخ الانتهاء', 'غير متوفر')
            ])
        else:
            table_data.append([num, "❌ فشل", "❌ فشل"])

    headers = ["📱 الرقم", "💵 الرصيد", "📅 الانتهاء"]
    table = tabulate(table_data, headers=headers, tablefmt="grid")

    response = ("⏰ *نتائج الاستعلام التلقائي*\n"
                "══════════════════════════\n"
                f"```\n{table}\n```\n"
                f"🔢 *عدد الأرقام:* {len(user_numbers)}\n\n"
                "✨ تم الانتهاء من عملية الاستعلام بنجاح")

    await processing_msg.delete()
    await context.bot.send_message(job.chat_id, response, parse_mode='Markdown')


async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    logger.error(msg="حدث خطأ في البوت:", exc_info=context.error)
    if update.message:
        await update.message.reply_text(
            "⛔ *عذراً، حدث خطأ غير متوقع*\n"
            "══════════════════════════\n"
            "لقد واجهنا مشكلة تقنية أثناء معالجة طلبك\n\n"
            "🔄 يرجى المحاولة مرة أخرى لاحقاً",
            parse_mode='Markdown')


def main():
    server_thread = threading.Thread(target=run)
    server_thread.start()

    application = ApplicationBuilder().token(
        "7563226990:AAFg2E5-f6EcVEjBK7ph5w8Q064P4daZlj0").build()

    conv_handler = ConversationHandler(
        entry_points=[CallbackQueryHandler(button_handler)],
        states={
            ADD_NUMBER: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_number)],
            DELETE_NUMBER: [CallbackQueryHandler(delete_number)],
            SCHEDULE_HOUR: [CallbackQueryHandler(schedule_hour)]
        },
        fallbacks=[CommandHandler("start", start)],
        per_message=True)

    application.add_handler(CommandHandler("start", start))
    application.add_handler(conv_handler)
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
    application.add_error_handler(error_handler)

    logger.info("🤖 البوت يعمل...")
    application.run_polling()


if __name__ == '__main__':
    main()
واريد مطابقة وقت انتهاء الرصيد للارقام في حال تبقاء خمس ايام اريد تغيير شكل الاشعار
ASSISTANT
لإضافة أوقات جدول استعلام إضافية (بخلاف الأوقات الموجودة 9 صباحاً، 12 ظهراً، 4 عصراً، 8 مساءً)، يمكنك ببساطة إضافة أزرار جديدة مع قيمة `callback_data` مناسبة في مكان عرض خيارات الجدولة، بالإضافة إلى التعامل مع القيم الجديدة في دالة `schedule_hour`.

كذلك بالنسبة لمطابقة وقت انتهاء الرصيد والتحقق إذا كان تبقى 5 أيام أو أقل لتغيير شكل الإشعار، يمكنك تعديل جزء بناء رسالة الإشعار بحيث يتضمن تحقق الفرق بين التاريخ الحالي وتاريخ انتهاء الاشتراك.

---

## 1. إضافة باقي الأوقات المطلوب جدولتها

مثلاً تريد إضافة 7 صباحاً و 10 مساءً مثلاً:

في الكود عند عرض أزرار الجدولة:

```python
elif query.data == 'schedule_query':
    keyboard = [[
        InlineKeyboardButton("🕖 7 صباحاً", callback_data='hour_7_AM'),
        InlineKeyboardButton("🕘 9 صباحاً", callback_data='hour_9_AM'),
        InlineKeyboardButton("🕛 12 ظهراً", callback_data='hour_12_PM')
    ],
                [
                    InlineKeyboardButton("🕓 4 عصراً", callback_data='hour_4_PM'),
                    InlineKeyboardButton("🕗 8 مساءً", callback_data='hour_8_PM'),
                    InlineKeyboardButton("🕥 10 مساءً", callback_data='hour_10_PM')
                ],
                [
                    InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')
                ]]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await query.edit_message_text(
        "⏰ *جدولة الاستعلام التلقائي*\n"
        "══════════════════════════\n"
        "📅 يرجى تحديد الوقت المفضل للاستعلام اليومي\n\n"
        "⏱️ سيتم إجراء الاستعلام تلقائياً في الوقت المحدد\n\n"
        "🏠 يمكنك العودة للمباشرة",
        reply_markup=reply_markup,
        parse_mode='Markdown')
    return SCHEDULE_HOUR
```

وبعد إضافة callback_data جديد في شكل `hour_7_AM` أو `hour_10_PM`، لا حاجة لتعديل دالة `schedule_hour` لأن الكود يعالج جميع الأوقات التي تبدأ بـ `hour_` بشكل ديناميكي.

---

## 2. تعديل شكل الإشعار عند انتهاء الرصيد خلال 5 أيام أو أقل

بما أن لديك ملف `info` يحتوي على مفتاح `'تاريخ الانتهاء'` مثل `"2024-06-30"`, تحتاج لتحليل هذا التاريخ ومقارنته مع التاريخ الحالي.

مثلاً، في دالة `send_balance_info` أو في المكان الذي تبني فيه رسالة الاستعلام، عدل الكود ليكون:

```python
async def send_balance_info(update, phone_number, info):
    if info:
        # جلب تاريخ الانتهاء بشكل صحيح
        end_date_str = info.get('تاريخ الانتهاء', None)
        warning_text = ""
        if end_date_str:
            try:
                end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                days_left = (end_date - now_date).days

                if days_left <= 5 and days_left >= 0:
                    warning_text = f"\n⚠️ *تنبيه:* تبقى فقط {days_left} يوم على انتهاء الباقة!"
                elif days_left < 0:
                    warning_text = "\n❗️ انتهت صلاحية الباقة."

            except Exception as e:
                logger.error(f"خطأ في تحليل تاريخ الانتهاء: {e}")
                # لا تفعل شيء خاص إذا فشل التحليل

        response = (
            f"🌟 *نتائج استعلام رصيد ADSL*\n"
            f"══════════════════════════\n"
            f"📱 *الرقم:* `{phone_number}`\n\n"
            f"💳 *الرصيد الحالي:* `{info.get('الرصيد الحالي', 'غير متوفر')}`\n"
            f"📦 *قيمة الباقة:* `{info.get('قيمة الباقة', 'غير متوفر')}`\n"
            f"⏳ *تاريخ الانتهاء:* `{end_date_str if end_date_str else 'غير متوفر'}`"
            f"{warning_text}\n\n"
            f"🔄 يمكنك إضافة هذا الرقم إلى المحفوظات للاستعلام عنه لاحقاً")
    else:
        response = ("⚠️ *عذراً، تعذر الحصول على معلومات الرصيد*\n"
                    "══════════════════════════\n"
                    "🛑 *الأسباب المحتملة:*\n"
                    "• الرقم غير مسجل في خدمة ADSL\n"
                    "• وجود مشكلة تقنية مؤقتة\n"
                    "• خطأ في الاتصال بالخادم\n\n"
                    "🔍 يرجى التأكد من الرقم والمحاولة لاحقاً")

    if isinstance(update, Update):
        await update.message.reply_text(response, parse_mode='Markdown')
    else:
        await update.reply_text(response, parse_mode='Markdown')
```

أيضًا من الأفضل تكرار هذا المنطق عند إرسال رسالة الجدولة التلقائية    `auto_query_callback` لتغيير الشكل هناك كذلك.

مثلاً داخل الحلقة التي تبني الجدول يمكن تعديل نص الرصيد ليشمل علامة تنبيه في حالة قرب انتهاء الاشتراك.

مثال قصير لتوضيح داخل `auto_query_callback`:

```python
for num in user_numbers:
    info = get_adsl_info(num)
    end_date_str = info.get('تاريخ الانتهاء', '') if info else ''
    remaining_days = None

    if end_date_str:
        try:
            end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
            now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
            remaining_days = (end_date - now_date).days
        except:
            remaining_days = None

    if info:
        balance = info.get('الرصيد الحالي', 'غير متوفر')
        # أضف علامة تنبيه إذا تبقى 5 أيام أو أقل
        if remaining_days is not None:
            if remaining_days < 0:
                balance += " ❗️ (انتهى الاشتراك)"
            elif remaining_days <= 5:
                balance += f" ⚠️ ({remaining_days} أيام متبقية)"
        table_data.append([
            num,
            balance,
            info.get('تاريخ الانتهاء', 'غير متوفر')
        ])
    else:
        table_data.append([num, "❌ فشل", "❌ فشل"])
```

---

## الملخص

- لإضافة أوقات: أضف أزرار مع callback_data منسقة مثل `hour_7_AM` و`hour_10_PM`.
- في دالة `schedule_hour` يتم التعامل مع أي قيمة تبدأ بـ `hour_` بشكل تلقائي.
- لمطابقة الوقت وإضافة تحذيرات عند تبقى 5 أيام أو أقل على انتهاء الرصيد، افحص تاريخ الانتهاء وقارن مع التاريخ الحالي، وأضف نص تحذير في الإشعار.
- طبق هذا في دالة `send_balance_info` وكذلك في تقرير الجدولة التلقائية داخل `auto_query_callback`.

---

إذا احتجت مساعدة في أي نقطة أو دمج التعديلات كاملة، فقط أخبرني!

turns-00079.parquet:38294

f035674896d28e785b066fd4
turn 2/4gpt-4.1-mini-2025-04-14ArabicYemen2032 words
degenerate_repetitionAbsentFinal dense release
USER
اعطيني الكود الكامل بعد التعديل لاقوم باستخدامة مباشرة
ASSISTANT
بالطبع، إليك الكود الكامل بعد إضافة الأوقات الإضافية (7 صباحاً، 10 مساءً) وتعديل رسالة الإشعار بحيث يتم التنبيه إذا تبقى 5 أيام أو أقل على انتهاء الباقة.

يرجى استبدال الجزء السابق من الكود بهذا الكود مباشرة:

```python
import requests
from bs4 import BeautifulSoup
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (ApplicationBuilder, CommandHandler, MessageHandler,
                          ContextTypes, filters, CallbackQueryHandler,
                          ConversationHandler)
import logging
import json
import os
from datetime import datetime, timedelta
import pytz
from tabulate import tabulate

# Flask و threading لخدمة الويب
from flask import Flask
import threading

app = Flask('')

@app.route('/')
def home():
    return "Bot is alive!"

def run():
    app.run(host="0.0.0.0", port=8080)


logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO)
logger = logging.getLogger(__name__)

ADD_NUMBER, DELETE_NUMBER, SCHEDULE_HOUR = range(3)

SAVED_NUMBERS_FILE = "saved_numbers.json"
SCHEDULED_TIMES_FILE = "scheduled_times.json"

YEMEN_TZ = pytz.timezone('Asia/Aden')

# --- التعديل: التحميل وحفظ حسب chat_id ---

def load_saved_numbers():
    if os.path.exists(SAVED_NUMBERS_FILE):
        try:
            with open(SAVED_NUMBERS_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"خطأ في تحميل الأرقام المحفوظة: {e}")
            return {}
    else:
        return {}

def load_scheduled_times():
    if os.path.exists(SCHEDULED_TIMES_FILE):
        try:
            with open(SCHEDULED_TIMES_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"خطأ في تحميل الأوقات المجدولة: {e}")
            return {}
    else:
        return {}

def save_numbers_to_file(numbers):
    try:
        with open(SAVED_NUMBERS_FILE, 'w', encoding='utf-8') as f:
            json.dump(numbers, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"خطأ في حفظ الأرقام المحفوظة: {e}")

def save_scheduled_times(times):
    try:
        with open(SCHEDULED_TIMES_FILE, 'w', encoding='utf-8') as f:
            json.dump(times, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"خطأ في حفظ الأوقات المجدولة: {e}")

saved_numbers_all = load_saved_numbers()
scheduled_times = load_scheduled_times()
scheduled_jobs = {}

ADSL_URL = "https://adsl-yemen.com/adsl4.php"

def get_adsl_info(phone_number):
    try:
        data = {"mobile": phone_number, "action": "query"}
        response = requests.post(ADSL_URL, data=data)

        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            results = soup.find_all("div", class_="result-box")
            data_dict = {}
            for box in results:
                title_div = box.find("div", class_="result-title")
                value_div = box.find("div", class_="result-value")
                if title_div and value_div:
                    title = title_div.text.strip()
                    value = value_div.text.strip()
                    data_dict[title] = value

            data_dict['timestamp'] = datetime.now(YEMEN_TZ).strftime("%Y-%m-%d %H:%M:%S")
            return data_dict
        else:
            return None
    except Exception as e:
        logger.error(f"حدث خطأ أثناء استعلام الرصيد: {e}")
        return None

async def send_balance_info(update, phone_number, info):
    if info:
        end_date_str = info.get('تاريخ الانتهاء', None)
        warning_text = ""
        if end_date_str:
            try:
                end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                days_left = (end_date - now_date).days

                if 0 <= days_left <= 5:
                    warning_text = f"\n⚠️ *تنبيه:* تبقى فقط {days_left} يوم على انتهاء الباقة!"
                elif days_left < 0:
                    warning_text = "\n❗️ انتهت صلاحية الباقة."

            except Exception as e:
                logger.error(f"خطأ في تحليل تاريخ الانتهاء: {e}")

        response = (
            f"🌟 *نتائج استعلام رصيد ADSL*\n"
            f"══════════════════════════\n"
            f"📱 *الرقم:* `{phone_number}`\n\n"
            f"💳 *الرصيد الحالي:* `{info.get('الرصيد الحالي', 'غير متوفر')}`\n"
            f"📦 *قيمة الباقة:* `{info.get('قيمة الباقة', 'غير متوفر')}`\n"
            f"⏳ *تاريخ الانتهاء:* `{end_date_str if end_date_str else 'غير متوفر'}`"
            f"{warning_text}\n\n"
            f"🔄 يمكنك إضافة هذا الرقم إلى المحفوظات للاستعلام عنه لاحقاً")
    else:
        response = ("⚠️ *عذراً، تعذر الحصول على معلومات الرصيد*\n"
                    "══════════════════════════\n"
                    "🛑 *الأسباب المحتملة:*\n"
                    "• الرقم غير مسجل في خدمة ADSL\n"
                    "• وجود مشكلة تقنية مؤقتة\n"
                    "• خطأ في الاتصال بالخادم\n\n"
                    "🔍 يرجى التأكد من الرقم والمحاولة لاحقاً")

    if isinstance(update, Update):
        await update.message.reply_text(response, parse_mode='Markdown')
    else:
        await update.reply_text(response, parse_mode='Markdown')


async def add_number(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    chat_id = str(update.effective_chat.id)
    phone_number = update.message.text.strip()

    if not phone_number.isdigit() or len(phone_number) != 8:
        await update.message.reply_text(
            "⚠️ *رقم غير صحيح!*\nيجب أن يتكون الرقم من 8 أرقام فقط.\nمثال: 71234567",
            parse_mode='Markdown')
        return ADD_NUMBER

    # تحقق وحفظ الرقم للمستخدم فقط
    if chat_id not in saved_numbers_all:
        saved_numbers_all[chat_id] = {}
    saved_numbers_all[chat_id][phone_number] = True
    save_numbers_to_file(saved_numbers_all)

    await update.message.reply_text(f"✅ تم حفظ الرقم {phone_number} بنجاح!",
                                    parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user = update.effective_user

    welcome_message = (
        f"✨ *مرحباً بك {user.first_name} في بوت استعلام رصيد ADSL اليمن* ✨\n\n"
        "🚀 *خدمات البوت المتكاملة:*\n"
        "• 📲 استعلام فوري عن رصيد أي خط ADSL\n"
        "• 💾 إدارة الأرقام المحفوظة بسهولة\n"
        "• ⏰ جدولة استعلامات تلقائية يومية\n"
        "• 📊 عرض التقارير المفصلة\n\n"
        "📌 *كيفية الاستخدام:*\n"
        "• أرسل رقم الهاتف مباشرة للاستعلام الفوري\n"
        "• أو استخدم القائمة أدناه للوصول إلى جميع الميزات")

    keyboard = [[
        InlineKeyboardButton("📲 استعلام فوري", callback_data='new_query'),
        InlineKeyboardButton("💾 إدارة الأرقام", callback_data='manage_numbers')
    ],
                [
                    InlineKeyboardButton("⏰ جدولة تلقائية",
                                         callback_data='schedule_query'),
                    InlineKeyboardButton("📊 الاستعلام الجماعي",
                                         callback_data='query_numbers')
                ], [InlineKeyboardButton("ℹ️ المساعدة", callback_data='help')]]
    reply_markup = InlineKeyboardMarkup(keyboard)

    if update.message:
        await update.message.reply_text(welcome_message,
                                        reply_markup=reply_markup,
                                        parse_mode='Markdown')
    else:
        await update.callback_query.edit_message_text(
            welcome_message, reply_markup=reply_markup, parse_mode='Markdown')


async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    chat_id = str(update.effective_chat.id)
    phone_number = update.message.text.strip()

    if not phone_number.isdigit() or len(phone_number) != 8:
        await update.message.reply_text(
            "⚠️ *يرجى إدخال رقم هاتف صحيح*\n"
            "══════════════════════════\n"
            "📌 *الشروط المطلوبة:*\n"
            "• يجب أن يتكون الرقم من 8 أرقام\n"
            "• يجب أن يحتوي على أرقام فقط\n"
            "• مثال: `71234567`",
            parse_mode='Markdown')
        return

    processing_msg = await update.message.reply_text(
        "⏳ *جاري معالجة طلبك...*\n"
        "══════════════════════════\n"
        "📡 يتم الآن الاتصال بخادم ADSL\n"
        "⏱️ قد يستغرق الأمر بضع ثوانٍ...",
        parse_mode='Markdown')

    info = get_adsl_info(phone_number)

    await processing_msg.delete()
    await send_balance_info(update, phone_number, info)

    # عرض خيار حفظ الرقم للمستخدم فقط
    if info:
        user_numbers = saved_numbers_all.get(chat_id, {})
        if phone_number not in user_numbers:
            keyboard = [[
                InlineKeyboardButton("💾 حفظ الرقم",
                                     callback_data=f'save_{phone_number}'),
                InlineKeyboardButton("🔄 استعلام جديد", callback_data='new_query')
            ], [InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')]]
            reply_markup = InlineKeyboardMarkup(keyboard)
            await update.message.reply_text(
                "❔ *هل ترغب في حفظ هذا الرقم للاستعلام عنه لاحقاً؟*",
                reply_markup=reply_markup,
                parse_mode='Markdown')


async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data == 'add_number':
        await query.edit_message_text(
            "📝 *إضافة رقم جديد للمحفوظات*\n"
            "══════════════════════════\n"
            "📱 يرجى إرسال رقم الهاتف المكون من 8 أرقام\n"
            "✏️ مثال: `71234567`\n\n"
            "🔙 للعودة اضغط /start",
            parse_mode='Markdown')
        return ADD_NUMBER

    elif query.data == 'manage_numbers':
        user_numbers = saved_numbers_all.get(chat_id, {})
        if not user_numbers:
            await query.edit_message_text(
                "⚠️ *لا توجد أرقام محفوظة*\n"
                "══════════════════════════\n"
                "لم تقم بإضافة أي أرقام إلى المحفوظات بعد\n\n"
                "➕ يمكنك إضافة أرقام جديدة من القائمة الرئيسية\n\n"
                "🏠 اضغط للعودة إلى الرئيسية",
                reply_markup=InlineKeyboardMarkup(
                    [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]),
                parse_mode='Markdown')
            return

        keyboard = []
        for num in user_numbers:
            keyboard.append([
                InlineKeyboardButton(f"🗑️ حذف {num}", callback_data=f'delete_{num}')
            ])
        keyboard.append([InlineKeyboardButton("➕ إضافة رقم جديد", callback_data='add_number')])
        keyboard.append([InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')])
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "🗂️ *إدارة الأرقام المحفوظة*\n"
            "══════════════════════════\n"
            "🔘 اختر الرقم الذي تريد إدارته:\n\n"
            "🗑️ زر الحذف - لإزالة الرقم من المحفوظات",
            reply_markup=reply_markup,
            parse_mode='Markdown')
        return DELETE_NUMBER

    elif query.data == 'query_numbers':
        user_numbers = saved_numbers_all.get(chat_id, {})
        if not user_numbers:
            await query.edit_message_text(
                "📊 *الاستعلام الجماعي*\n"
                "══════════════════════════\n"
                "⚠️ لا توجد أرقام محفوظة حالياً\n\n"
                "➕ يمكنك إضافة أرقام جديدة من القائمة الرئيسية\n\n"
                "🏠 اضغط للعودة إلى الرئيسية",
                reply_markup=InlineKeyboardMarkup(
                    [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]),
                parse_mode='Markdown')
            return

        processing_msg = await query.message.reply_text(
            "⏳ *جاري استعلام الأرقام المحفوظة*\n"
            "══════════════════════════\n"
            "📡 يتم الآن جلب أحدث المعلومات عن جميع الأرقام\n"
            "⏱️ قد تستغرق العملية بعض الوقت...",
            parse_mode='Markdown')

        table_data = []
        for num in user_numbers:
            info = get_adsl_info(num)
            end_date_str = info.get('تاريخ الانتهاء', '') if info else ''
            remaining_days = None

            if end_date_str:
                try:
                    end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                    now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                    remaining_days = (end_date - now_date).days
                except:
                    remaining_days = None

            if info:
                balance = info.get('الرصيد الحالي', 'غير متوفر')
                if remaining_days is not None:
                    if remaining_days < 0:
                        balance += " ❗️ (انتهى الاشتراك)"
                    elif remaining_days <= 5:
                        balance += f" ⚠️ ({remaining_days} أيام متبقية)"
                table_data.append([
                    num,
                    balance,
                    info.get('تاريخ الانتهاء', 'غير متوفر')
                ])
            else:
                table_data.append([num, "❌ فشل", "❌ فشل"])

        headers = ["📱 الرقم", "💵 الرصيد", "📅 الانتهاء"]
        table = tabulate(table_data, headers=headers, tablefmt="grid")

        response = ("📊 *نتائج الاستعلام الجماعي*\n"
                    "══════════════════════════\n"
                    f"```\n{table}\n```\n"
                    f"🔢 *عدد الأرقام:* {len(user_numbers)}\n\n"
                    "✨ تم الانتهاء من عملية الاستعلام بنجاح")

        await processing_msg.delete()
        await query.message.reply_text(response, parse_mode='Markdown')

    elif query.data == 'schedule_query':
        keyboard = [[
            InlineKeyboardButton("🕖 7 صباحاً", callback_data='hour_7_AM'),
            InlineKeyboardButton("🕘 9 صباحاً", callback_data='hour_9_AM'),
            InlineKeyboardButton("🕛 12 ظهراً", callback_data='hour_12_PM')
        ],
                    [
                        InlineKeyboardButton("🕓 4 عصراً", callback_data='hour_4_PM'),
                        InlineKeyboardButton("🕗 8 مساءً", callback_data='hour_8_PM'),
                        InlineKeyboardButton("🕥 10 مساءً", callback_data='hour_10_PM')
                    ],
                    [
                        InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')
                    ]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "⏰ *جدولة الاستعلام التلقائي*\n"
            "══════════════════════════\n"
            "📅 يرجى تحديد الوقت المفضل للاستعلام اليومي\n\n"
            "⏱️ سيتم إجراء الاستعلام تلقائياً في الوقت المحدد\n\n"
            "🏠 يمكنك العودة للمباشرة",
            reply_markup=reply_markup,
            parse_mode='Markdown')
        return SCHEDULE_HOUR

    elif query.data == 'help':
        help_text = ("ℹ️ *دليل استخدام البوت*\n"
                     "══════════════════════════\n"
                     "🔹 *استعلام فوري*:\n"
                     "   - أرسل الرقم مباشرة (8 أرقام)\n"
                     "   - أو اضغط على 'استعلام فوري'\n\n"
                     "🔹 *إدارة الأرقام*:\n"
                     "   - حفظ الأرقام للاستعلام الجماعي\n"
                     "   - حذف الأرقام المحفوظة\n\n"
                     "🔹 *الاستعلام الجماعي*:\n"
                     "   - استعلام جميع الأرقام المحفوظة دفعة واحدة\n\n"
                     "🔹 *جدولة استعلام*:\n"
                     "   - ضبط استعلام تلقائي يومي في وقت محدد\n\n"
                     "📌 *ملاحظات مهمة*:\n"
                     "   - يجب أن يتكون الرقم من 8 أرقام\n"
                     "   - الخدمة متوفرة لجميع عملاء ADSL اليمن\n\n"
                     "🏠 اضغط للعودة إلى الرئيسية")
        keyboard = [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(help_text, reply_markup=reply_markup, parse_mode='Markdown')

    elif query.data == 'back':
        await start(update, context)

    elif query.data == 'new_query':
        keyboard = [[InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')]]
        await query.edit_message_text(
            "🔍 *استعلام جديد*\n"
            "══════════════════════════\n"
            "📱 يرجى إرسال رقم الهاتف المكون من 8 أرقام\n"
            "✏️ مثال: `71234567`",
            reply_markup=InlineKeyboardMarkup(keyboard),
            parse_mode='Markdown')

    elif query.data.startswith('save_'):
        phone_number = query.data[5:]
        if chat_id not in saved_numbers_all:
            saved_numbers_all[chat_id] = {}
        saved_numbers_all[chat_id][phone_number] = True
        save_numbers_to_file(saved_numbers_all)
        await query.edit_message_text(
            f"✅ *تم الحفظ بنجاح*\n"
            f"══════════════════════════\n"
            f"تم حفظ الرقم `{phone_number}` في المحفوظات\n\n"
            f"📋 يمكنك الآن إجراء استعلام جماعي أو جدولة استعلامات تلقائية",
            parse_mode='Markdown')
        await start(update, context)


async def delete_number(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data.startswith('delete_'):
        num_to_delete = query.data[7:]
        user_numbers = saved_numbers_all.get(chat_id, {})
        if num_to_delete in user_numbers:
            del user_numbers[num_to_delete]
            save_numbers_to_file(saved_numbers_all)
            await query.edit_message_text(
                f"✅ *تم الحذف بنجاح*\n"
                f"══════════════════════════\n"
                f"• *الرقم المحذوف:* `{num_to_delete}`\n\n"
                f"📋 يمكنك إضافة أرقام جديدة من القائمة الرئيسية",
                parse_mode='Markdown')
        else:
            await query.edit_message_text(
                "⚠️ *تعذر الحذف*\n"
                "══════════════════════════\n"
                "• الرقم المطلوب غير موجود في المحفوظات\n\n"
                "📋 يرجى التحقق من الأرقام المحفوظة والمحاولة مرة أخرى",
                parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def schedule_hour(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data.startswith('hour_'):
        time_parts = query.data.split('_')  # ['hour', '7', 'AM'] مثلاً
        hour = int(time_parts[1])
        ampm = time_parts[2]

        # تحويل إلى 24 ساعة
        if ampm == 'PM' and hour < 12:
            hour += 12
        elif ampm == 'AM' and hour == 12:
            hour = 0

        # حفظ الجدولة (وقت واحد فقط لكل مستخدم)
        scheduled_times[chat_id] = {'hour': hour, 'minute': 0, 'ampm': ampm}
        save_scheduled_times(scheduled_times)

        now = datetime.now(YEMEN_TZ)
        scheduled_time = now.replace(hour=hour, minute=0, second=0, microsecond=0)
        if scheduled_time < now:
            scheduled_time += timedelta(days=1)

        # إزالة الجدولة السابقة إن وجدت
        if chat_id in scheduled_jobs:
            scheduled_jobs[chat_id].schedule_removal()

        try:
            job = context.application.job_queue.run_repeating(
                auto_query_callback,
                interval=timedelta(days=1),
                first=timedelta(seconds=(scheduled_time - now).total_seconds()),
                chat_id=int(chat_id))
            scheduled_jobs[chat_id] = job

            await query.edit_message_text(
                f"✅ *تمت الجدولة بنجاح*\n"
                f"══════════════════════════\n"
                f"• *وقت الاستعلام:* {time_parts[1]} {'صباحاً' if ampm == 'AM' else 'مساءً'}\n"
                f"• سيبدأ الاستعلام التلقائي يومياً في الوقت المحدد\n\n"
                f"📋 يمكنك تعديل الإعدادات مرة واحدة يومياً من القائمة الرئيسية",
                parse_mode='Markdown')
        except Exception as e:
            logger.error(f"خطأ في الجدولة: {e}")
            await query.edit_message_text(
                "⚠️ *تعذر إعداد الجدولة*\n"
                "══════════════════════════\n"
                "حدث خطأ تقني أثناء محاولة جدولة الاستعلام\n\n"
                "🔄 يرجى المحاولة مرة أخرى",
                parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def auto_query_callback(context: ContextTypes.DEFAULT_TYPE):
    job = context.job
    chat_id = str(job.chat_id)

    user_numbers = saved_numbers_all.get(chat_id, {})
    if not user_numbers:
        await context.bot.send_message(
            job.chat_id, "⚠️ *لا توجد أرقام محفوظة*\n"
            "══════════════════════════\n"
            "لم يتم العثور على أي أرقام محفوظة للاستعلام عنها\n\n"
            "➕ يرجى إضافة أرقام أولاً من القائمة الرئيسية",
            parse_mode='Markdown')
        return

    processing_msg = await context.bot.send_message(
        job.chat_id, "⏳ *جاري تنفيذ الاستعلام التلقائي*\n"
        "══════════════════════════\n"
        "📡 يتم الآن جلب أحدث المعلومات عن الأرقام المحفوظة\n"
        "⏱️ يرجى الانتظار...",
        parse_mode='Markdown')

    table_data = []
    for num in user_numbers:
        info = get_adsl_info(num)
        end_date_str = info.get('تاريخ الانتهاء', '') if info else ''
        remaining_days = None

        if end_date_str:
            try:
                end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                remaining_days = (end_date - now_date).days
            except:
                remaining_days = None

        if info:
            balance = info.get('الرصيد الحالي', 'غير متوفر')
            if remaining_days is not None:
                if remaining_days < 0:
                    balance += " ❗️ (انتهى الاشتراك)"
                elif remaining_days <= 5:
                    balance += f" ⚠️ ({remaining_days} أيام متبقية)"
            table_data.append([
                num,
                balance,
                info.get('تاريخ الانتهاء', 'غير متوفر')
            ])
        else:
            table_data.append([num, "❌ فشل", "❌ فشل"])

    headers = ["📱 الرقم", "💵 الرصيد", "📅 الانتهاء"]
    table = tabulate(table_data, headers=headers, tablefmt="grid")

    response = ("⏰ *نتائج الاستعلام التلقائي*\n"
                "══════════════════════════\n"
                f"```\n{table}\n```\n"
                f"🔢 *عدد الأرقام:* {len(user_numbers)}\n\n"
                "✨ تم الانتهاء من عملية الاستعلام بنجاح")

    await processing_msg.delete()
    await context.bot.send_message(job.chat_id, response, parse_mode='Markdown')


async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    logger.error(msg="حدث خطأ في البوت:", exc_info=context.error)
    if update.message:
        await update.message.reply_text(
            "⛔ *عذراً، حدث خطأ غير متوقع*\n"
            "══════════════════════════\n"
            "لقد واجهنا مشكلة تقنية أثناء معالجة طلبك\n\n"
            "🔄 يرجى المحاولة مرة أخرى لاحقاً",
            parse_mode='Markdown')


def main():
    server_thread = threading.Thread(target=run)
    server_thread.start()

    application = ApplicationBuilder().token(
        "7563226990:AAFg2E5-f6EcVEjBK7ph5w8Q064P4daZlj0").build()

    conv_handler = ConversationHandler(
        entry_points=[CallbackQueryHandler(button_handler)],
        states={
            ADD_NUMBER: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_number)],
            DELETE_NUMBER: [CallbackQueryHandler(delete_number)],
            SCHEDULE_HOUR: [CallbackQueryHandler(schedule_hour)]
        },
        fallbacks=[CommandHandler("start", start)],
        per_message=True)

    application.add_handler(CommandHandler("start", start))
    application.add_handler(conv_handler)
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
    application.add_error_handler(error_handler)

    logger.info("🤖 البوت يعمل...")
    application.run_polling()


if __name__ == '__main__':
    main()
```

---

## ملاحظات هامة
- الكود أضاف خيارين وقت جدولة جديدين: 7 صباحاً و10 مساءً (يمكنك تعديل أو إضافة أوقات أخرى بنفس النمط).
- تم تعديل رسالة الاستعلام الفردي (`send_balance_info`) وجدول الاستعلام الجماعي والتلقائي (`query_numbers`, `auto_query_callback`) لإظهار تحذير إذا تبقى 5 أيام أو أقل أو انتهاء الاشتراك.
- تأكد من استبدال `token` في `ApplicationBuilder()` بالتوكن الخاص بالبوت عندك.
- يرجى اختبار الكود في بيئة مناسبة وتجربته.

إذا أردت أي مساعدة إضافية في تعديل أو شرح أي جزء لا تتردد بمراسلتنا. بالتوفيق!

turns-00079.parquet:38295

df780aceabc7a7af751f6cec
turn 3/4gpt-4.1-mini-2025-04-14ArabicYemen2244 words
degenerate_repetitionAbsentFinal dense release
USER
هل هذا الكود جاهز لاستخدامة في موقع https://pulsetic.com/uptimerobot-alternative/
 للعلم الكود

import requests
from bs4 import BeautifulSoup
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (ApplicationBuilder, CommandHandler, MessageHandler,
                          ContextTypes, filters, CallbackQueryHandler,
                          ConversationHandler)
import logging
import json
import os
from datetime import datetime, timedelta
import pytz
from tabulate import tabulate

# Flask و threading لخدمة الويب
from flask import Flask
import threading

app = Flask('')


@app.route('/')
def home():
    return "Bot is alive!"


def run():
    app.run(host="0.0.0.0", port=8080)


logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO)
logger = logging.getLogger(__name__)

ADD_NUMBER, DELETE_NUMBER, SCHEDULE_HOUR = range(3)

SAVED_NUMBERS_FILE = "saved_numbers.json"
SCHEDULED_TIMES_FILE = "scheduled_times.json"

YEMEN_TZ = pytz.timezone('Asia/Aden')

# --- التعديل: التحميل وحفظ حسب chat_id ---


def load_saved_numbers():
    if os.path.exists(SAVED_NUMBERS_FILE):
        try:
            with open(SAVED_NUMBERS_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"خطأ في تحميل الأرقام المحفوظة: {e}")
            return {}
    else:
        return {}


def load_scheduled_times():
    if os.path.exists(SCHEDULED_TIMES_FILE):
        try:
            with open(SCHEDULED_TIMES_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"خطأ في تحميل الأوقات المجدولة: {e}")
            return {}
    else:
        return {}


def save_numbers_to_file(numbers):
    try:
        with open(SAVED_NUMBERS_FILE, 'w', encoding='utf-8') as f:
            json.dump(numbers, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"خطأ في حفظ الأرقام المحفوظة: {e}")


def save_scheduled_times(times):
    try:
        with open(SCHEDULED_TIMES_FILE, 'w', encoding='utf-8') as f:
            json.dump(times, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"خطأ في حفظ الأوقات المجدولة: {e}")


saved_numbers_all = load_saved_numbers()
scheduled_times = load_scheduled_times()
scheduled_jobs = {}

ADSL_URL = "https://adsl-yemen.com/adsl4.php"


def get_adsl_info(phone_number):
    try:
        data = {"mobile": phone_number, "action": "query"}
        response = requests.post(ADSL_URL, data=data)

        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            results = soup.find_all("div", class_="result-box")
            data_dict = {}
            for box in results:
                title_div = box.find("div", class_="result-title")
                value_div = box.find("div", class_="result-value")
                if title_div and value_div:
                    title = title_div.text.strip()
                    value = value_div.text.strip()
                    data_dict[title] = value

            data_dict['timestamp'] = datetime.now(YEMEN_TZ).strftime(
                "%Y-%m-%d %H:%M:%S")
            return data_dict
        else:
            return None
    except Exception as e:
        logger.error(f"حدث خطأ أثناء استعلام الرصيد: {e}")
        return None


async def send_balance_info(update, phone_number, info):
    if info:
        end_date_str = info.get('تاريخ الانتهاء', None)
        warning_text = ""
        if end_date_str:
            try:
                end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                days_left = (end_date - now_date).days

                if 0 <= days_left <= 5:
                    warning_text = f"\n⚠️ *تنبيه:* تبقى فقط {days_left} يوم على انتهاء الباقة!"
                elif days_left < 0:
                    warning_text = "\n❗️ انتهت صلاحية الباقة."

            except Exception as e:
                logger.error(f"خطأ في تحليل تاريخ الانتهاء: {e}")

        response = (
            f"🌟 *نتائج استعلام رصيد ADSL*\n"
            f"══════════════════════════\n"
            f"📱 *الرقم:* {phone_number}\n\n"
            f"💳 *الرصيد الحالي:* {info.get('الرصيد الحالي', 'غير متوفر')}\n"
            f"📦 *قيمة الباقة:* {info.get('قيمة الباقة', 'غير متوفر')}\n"
            f"⏳ *تاريخ الانتهاء:* {end_date_str if end_date_str else 'غير متوفر'}"
            f"{warning_text}\n\n"
            f"🔄 يمكنك إضافة هذا الرقم إلى المحفوظات للاستعلام عنه لاحقاً")
    else:
        response = ("⚠️ *عذراً، تعذر الحصول على معلومات الرصيد*\n"
                    "══════════════════════════\n"
                    "🛑 *الأسباب المحتملة:*\n"
                    "• الرقم غير مسجل في خدمة ADSL\n"
                    "• وجود مشكلة تقنية مؤقتة\n"
                    "• خطأ في الاتصال بالخادم\n\n"
                    "🔍 يرجى التأكد من الرقم والمحاولة لاحقاً")

    if isinstance(update, Update):
        await update.message.reply_text(response, parse_mode='Markdown')
    else:
        await update.reply_text(response, parse_mode='Markdown')


async def add_number(update: Update,
                     context: ContextTypes.DEFAULT_TYPE) -> int:
    chat_id = str(update.effective_chat.id)
    phone_number = update.message.text.strip()

    if not phone_number.isdigit() or len(phone_number) != 8:
        await update.message.reply_text(
            "⚠️ *رقم غير صحيح!*\nيجب أن يتكون الرقم من 8 أرقام فقط.\nمثال: 71234567",
            parse_mode='Markdown')
        return ADD_NUMBER

    # تحقق وحفظ الرقم للمستخدم فقط
    if chat_id not in saved_numbers_all:
        saved_numbers_all[chat_id] = {}
    saved_numbers_all[chat_id][phone_number] = True
    save_numbers_to_file(saved_numbers_all)

    await update.message.reply_text(f"✅ تم حفظ الرقم {phone_number} بنجاح!",
                                    parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user = update.effective_user

    welcome_message = (
        f"✨ *مرحباً بك {user.first_name} في بوت استعلام رصيد ADSL اليمن* ✨\n\n"
        "🚀 *خدمات البوت المتكاملة:*\n"
        "• 📲 استعلام فوري عن رصيد أي خط ADSL\n"
        "• 💾 إدارة الأرقام المحفوظة بسهولة\n"
        "• ⏰ جدولة استعلامات تلقائية يومية\n"
        "• 📊 عرض التقارير المفصلة\n\n"
        "📌 *كيفية الاستخدام:*\n"
        "• أرسل رقم الهاتف مباشرة للاستعلام الفوري\n"
        "• أو استخدم القائمة أدناه للوصول إلى جميع الميزات")

    keyboard = [[
        InlineKeyboardButton("📲 استعلام فوري", callback_data='new_query'),
        InlineKeyboardButton("💾 إدارة الأرقام", callback_data='manage_numbers')
    ],
                [
                    InlineKeyboardButton("⏰ جدولة تلقائية",
                                         callback_data='schedule_query'),
                    InlineKeyboardButton("📊 الاستعلام الجماعي",
                                         callback_data='query_numbers')
                ], [InlineKeyboardButton("ℹ️ المساعدة", callback_data='help')]]
    reply_markup = InlineKeyboardMarkup(keyboard)

    if update.message:
        await update.message.reply_text(welcome_message,
                                        reply_markup=reply_markup,
                                        parse_mode='Markdown')
    else:
        await update.callback_query.edit_message_text(
            welcome_message, reply_markup=reply_markup, parse_mode='Markdown')


async def handle_message(update: Update,
                         context: ContextTypes.DEFAULT_TYPE) -> None:
    chat_id = str(update.effective_chat.id)
    phone_number = update.message.text.strip()

    if not phone_number.isdigit() or len(phone_number) != 8:
        await update.message.reply_text(
            "⚠️ *يرجى إدخال رقم هاتف صحيح*\n"
            "══════════════════════════\n"
            "📌 *الشروط المطلوبة:*\n"
            "• يجب أن يتكون الرقم من 8 أرقام\n"
            "• يجب أن يحتوي على أرقام فقط\n"
            "• مثال: 71234567",
            parse_mode='Markdown')
        return

    processing_msg = await update.message.reply_text(
        "⏳ *جاري معالجة طلبك...*\n"
        "══════════════════════════\n"
        "📡 يتم الآن الاتصال بخادم ADSL\n"
        "⏱️ قد يستغرق الأمر بضع ثوانٍ...",
        parse_mode='Markdown')

    info = get_adsl_info(phone_number)

    await processing_msg.delete()
    await send_balance_info(update, phone_number, info)

    # عرض خيار حفظ الرقم للمستخدم فقط
    if info:
        user_numbers = saved_numbers_all.get(chat_id, {})
        if phone_number not in user_numbers:
            keyboard = [[
                InlineKeyboardButton("💾 حفظ الرقم",
                                     callback_data=f'save_{phone_number}'),
                InlineKeyboardButton("🔄 استعلام جديد",
                                     callback_data='new_query')
            ],
                        [
                            InlineKeyboardButton("🏠 القائمة الرئيسية",
                                                 callback_data='back')
                        ]]
            reply_markup = InlineKeyboardMarkup(keyboard)
            await update.message.reply_text(
                "❔ *هل ترغب في حفظ هذا الرقم للاستعلام عنه لاحقاً؟*",
                reply_markup=reply_markup,
                parse_mode='Markdown')


async def button_handler(update: Update,
                         context: ContextTypes.DEFAULT_TYPE) -> None:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data == 'add_number':
        await query.edit_message_text(
            "📝 *إضافة رقم جديد للمحفوظات*\n"
            "══════════════════════════\n"
            "📱 يرجى إرسال رقم الهاتف المكون من 8 أرقام\n"
            "✏️ مثال: 71234567\n\n"
            "🔙 للعودة اضغط /start",
            parse_mode='Markdown')
        return ADD_NUMBER

    elif query.data == 'manage_numbers':
        user_numbers = saved_numbers_all.get(chat_id, {})
        if not user_numbers:
            await query.edit_message_text(
                "⚠️ *لا توجد أرقام محفوظة*\n"
                "══════════════════════════\n"
                "لم تقم بإضافة أي أرقام إلى المحفوظات بعد\n\n"
                "➕ يمكنك إضافة أرقام جديدة من القائمة الرئيسية\n\n"
                "🏠 اضغط للعودة إلى الرئيسية",
                reply_markup=InlineKeyboardMarkup([[
                    InlineKeyboardButton("🏠 الرئيسية", callback_data='back')
                ]]),
                parse_mode='Markdown')
            return

        keyboard = []
        for num in user_numbers:
            keyboard.append([
                InlineKeyboardButton(f"🗑️ حذف {num}",
                                     callback_data=f'delete_{num}')
            ])
        keyboard.append([
            InlineKeyboardButton("➕ إضافة رقم جديد",
                                 callback_data='add_number')
        ])
        keyboard.append(
            [InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')])
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "🗂️ *إدارة الأرقام المحفوظة*\n"
            "══════════════════════════\n"
            "🔘 اختر الرقم الذي تريد إدارته:\n\n"
            "🗑️ زر الحذف - لإزالة الرقم من المحفوظات",
            reply_markup=reply_markup,
            parse_mode='Markdown')
        return DELETE_NUMBER

    elif query.data == 'query_numbers':
        user_numbers = saved_numbers_all.get(chat_id, {})
        if not user_numbers:
            await query.edit_message_text(
                "📊 *الاستعلام الجماعي*\n"
                "══════════════════════════\n"
                "⚠️ لا توجد أرقام محفوظة حالياً\n\n"
                "➕ يمكنك إضافة أرقام جديدة من القائمة الرئيسية\n\n"
                "🏠 اضغط للعودة إلى الرئيسية",
                reply_markup=InlineKeyboardMarkup([[
                    InlineKeyboardButton("🏠 الرئيسية", callback_data='back')
                ]]),
                parse_mode='Markdown')
            return

        processing_msg = await query.message.reply_text(
            "⏳ *جاري استعلام الأرقام المحفوظة*\n"
            "══════════════════════════\n"
            "📡 يتم الآن جلب أحدث المعلومات عن جميع الأرقام\n"
            "⏱️ قد تستغرق العملية بعض الوقت...",
            parse_mode='Markdown')

        table_data = []
        for num in user_numbers:
            info = get_adsl_info(num)
            end_date_str = info.get('تاريخ الانتهاء', '') if info else ''
            remaining_days = None

            if end_date_str:
                try:
                    end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                    now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                    remaining_days = (end_date - now_date).days
                except:
                    remaining_days = None

            if info:
                balance = info.get('الرصيد الحالي', 'غير متوفر')
                if remaining_days is not None:
                    if remaining_days < 0:
                        balance += " ❗️ (انتهى الاشتراك)"
                    elif remaining_days <= 5:
                        balance += f" ⚠️ ({remaining_days} أيام متبقية)"
                table_data.append(
                    [num, balance,
                     info.get('تاريخ الانتهاء', 'غير متوفر')])
            else:
                table_data.append([num, "❌ فشل", "❌ فشل"])

        headers = ["📱 الرقم", "💵 الرصيد", "📅 الانتهاء"]
        table = tabulate(table_data, headers=headers, tablefmt="grid")

        response = ("📊 *نتائج الاستعلام الجماعي*\n"
                    "══════════════════════════\n"
                    f"
\n{table}\n
\n"
                    f"🔢 *عدد الأرقام:* {len(user_numbers)}\n\n"
                    "✨ تم الانتهاء من عملية الاستعلام بنجاح")

        await processing_msg.delete()
        await query.message.reply_text(response, parse_mode='Markdown')

    elif query.data == 'schedule_query':
        keyboard = [[
            InlineKeyboardButton("🕖 7 صباحاً", callback_data='hour_7_AM'),
            InlineKeyboardButton("🕘 9 صباحاً", callback_data='hour_9_AM'),
            InlineKeyboardButton("🕛 12 ظهراً", callback_data='hour_12_PM')
        ],
                    [
                        InlineKeyboardButton("🕓 4 عصراً",
                                             callback_data='hour_4_PM'),
                        InlineKeyboardButton("🕗 8 مساءً",
                                             callback_data='hour_8_PM'),
                        InlineKeyboardButton("🕥 10 مساءً",
                                             callback_data='hour_10_PM')
                    ],
                    [
                        InlineKeyboardButton("🏠 القائمة الرئيسية",
                                             callback_data='back')
                    ]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(
            "⏰ *جدولة الاستعلام التلقائي*\n"
            "══════════════════════════\n"
            "📅 يرجى تحديد الوقت المفضل للاستعلام اليومي\n\n"
            "⏱️ سيتم إجراء الاستعلام تلقائياً في الوقت المحدد\n\n"
            "🏠 يمكنك العودة للمباشرة",
            reply_markup=reply_markup,
            parse_mode='Markdown')
        return SCHEDULE_HOUR

    elif query.data == 'help':
        help_text = ("ℹ️ *دليل استخدام البوت*\n"
                     "══════════════════════════\n"
                     "🔹 *استعلام فوري*:\n"
                     "   - أرسل الرقم مباشرة (8 أرقام)\n"
                     "   - أو اضغط على 'استعلام فوري'\n\n"
                     "🔹 *إدارة الأرقام*:\n"
                     "   - حفظ الأرقام للاستعلام الجماعي\n"
                     "   - حذف الأرقام المحفوظة\n\n"
                     "🔹 *الاستعلام الجماعي*:\n"
                     "   - استعلام جميع الأرقام المحفوظة دفعة واحدة\n\n"
                     "🔹 *جدولة استعلام*:\n"
                     "   - ضبط استعلام تلقائي يومي في وقت محدد\n\n"
                     "📌 *ملاحظات مهمة*:\n"
                     "   - يجب أن يتكون الرقم من 8 أرقام\n"
                     "   - الخدمة متوفرة لجميع عملاء ADSL اليمن\n\n"
                     "🏠 اضغط للعودة إلى الرئيسية")
        keyboard = [[InlineKeyboardButton("🏠 الرئيسية", callback_data='back')]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(help_text,
                                      reply_markup=reply_markup,
                                      parse_mode='Markdown')

    elif query.data == 'back':
        await start(update, context)

    elif query.data == 'new_query':
        keyboard = [[
            InlineKeyboardButton("🏠 القائمة الرئيسية", callback_data='back')
        ]]
        await query.edit_message_text(
            "🔍 *استعلام جديد*\n"
            "══════════════════════════\n"
            "📱 يرجى إرسال رقم الهاتف المكون من 8 أرقام\n"
            "✏️ مثال: 71234567",
            reply_markup=InlineKeyboardMarkup(keyboard),
            parse_mode='Markdown')

    elif query.data.startswith('save_'):
        phone_number = query.data[5:]
        if chat_id not in saved_numbers_all:
            saved_numbers_all[chat_id] = {}
        saved_numbers_all[chat_id][phone_number] = True
        save_numbers_to_file(saved_numbers_all)
        await query.edit_message_text(
            f"✅ *تم الحفظ بنجاح*\n"
            f"══════════════════════════\n"
            f"تم حفظ الرقم {phone_number} في المحفوظات\n\n"
            f"📋 يمكنك الآن إجراء استعلام جماعي أو جدولة استعلامات تلقائية",
            parse_mode='Markdown')
        await start(update, context)


async def delete_number(update: Update,
                        context: ContextTypes.DEFAULT_TYPE) -> int:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data.startswith('delete_'):
        num_to_delete = query.data[7:]
        user_numbers = saved_numbers_all.get(chat_id, {})
        if num_to_delete in user_numbers:
            del user_numbers[num_to_delete]
            save_numbers_to_file(saved_numbers_all)
            await query.edit_message_text(
                f"✅ *تم الحذف بنجاح*\n"
                f"══════════════════════════\n"
                f"• *الرقم المحذوف:* {num_to_delete}\n\n"
                f"📋 يمكنك إضافة أرقام جديدة من القائمة الرئيسية",
                parse_mode='Markdown')
        else:
            await query.edit_message_text(
                "⚠️ *تعذر الحذف*\n"
                "══════════════════════════\n"
                "• الرقم المطلوب غير موجود في المحفوظات\n\n"
                "📋 يرجى التحقق من الأرقام المحفوظة والمحاولة مرة أخرى",
                parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def schedule_hour(update: Update,
                        context: ContextTypes.DEFAULT_TYPE) -> int:
    query = update.callback_query
    chat_id = str(query.message.chat.id)
    await query.answer()

    if query.data.startswith('hour_'):
        time_parts = query.data.split('_')  # ['hour', '7', 'AM'] مثلاً
        hour = int(time_parts[1])
        ampm = time_parts[2]

        # تحويل إلى 24 ساعة
        if ampm == 'PM' and hour < 12:
            hour += 12
        elif ampm == 'AM' and hour == 12:
            hour = 0

        # حفظ الجدولة (وقت واحد فقط لكل مستخدم)
        scheduled_times[chat_id] = {'hour': hour, 'minute': 0, 'ampm': ampm}
        save_scheduled_times(scheduled_times)

        now = datetime.now(YEMEN_TZ)
        scheduled_time = now.replace(hour=hour,
                                     minute=0,
                                     second=0,
                                     microsecond=0)
        if scheduled_time < now:
            scheduled_time += timedelta(days=1)

        # إزالة الجدولة السابقة إن وجدت
        if chat_id in scheduled_jobs:
            scheduled_jobs[chat_id].schedule_removal()

        try:
            job = context.application.job_queue.run_repeating(
                auto_query_callback,
                interval=timedelta(days=1),
                first=timedelta(seconds=(scheduled_time -
                                         now).total_seconds()),
                chat_id=int(chat_id))
            scheduled_jobs[chat_id] = job

            await query.edit_message_text(
                f"✅ *تمت الجدولة بنجاح*\n"
                f"══════════════════════════\n"
                f"• *وقت الاستعلام:* {time_parts[1]} {'صباحاً' if ampm == 'AM' else 'مساءً'}\n"
                f"• سيبدأ الاستعلام التلقائي يومياً في الوقت المحدد\n\n"
                f"📋 يمكنك تعديل الإعدادات مرة واحدة يومياً من القائمة الرئيسية",
                parse_mode='Markdown')
        except Exception as e:
            logger.error(f"خطأ في الجدولة: {e}")
            await query.edit_message_text(
                "⚠️ *تعذر إعداد الجدولة*\n"
                "══════════════════════════\n"
                "حدث خطأ تقني أثناء محاولة جدولة الاستعلام\n\n"
                "🔄 يرجى المحاولة مرة أخرى",
                parse_mode='Markdown')

    await start(update, context)
    return ConversationHandler.END


async def auto_query_callback(context: ContextTypes.DEFAULT_TYPE):
    job = context.job
    chat_id = str(job.chat_id)

    user_numbers = saved_numbers_all.get(chat_id, {})
    if not user_numbers:
        await context.bot.send_message(
            job.chat_id, "⚠️ *لا توجد أرقام محفوظة*\n"
            "══════════════════════════\n"
            "لم يتم العثور على أي أرقام محفوظة للاستعلام عنها\n\n"
            "➕ يرجى إضافة أرقام أولاً من القائمة الرئيسية",
            parse_mode='Markdown')
        return

    processing_msg = await context.bot.send_message(
        job.chat_id, "⏳ *جاري تنفيذ الاستعلام التلقائي*\n"
        "══════════════════════════\n"
        "📡 يتم الآن جلب أحدث المعلومات عن الأرقام المحفوظة\n"
        "⏱️ يرجى الانتظار...",
        parse_mode='Markdown')

    table_data = []
    for num in user_numbers:
        info = get_adsl_info(num)
        end_date_str = info.get('تاريخ الانتهاء', '') if info else ''
        remaining_days = None

        if end_date_str:
            try:
                end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
                now_date = datetime.now(YEMEN_TZ).replace(tzinfo=None)
                remaining_days = (end_date - now_date).days
            except:
                remaining_days = None

        if info:
            balance = info.get('الرصيد الحالي', 'غير متوفر')
            if remaining_days is not None:
                if remaining_days < 0:
                    balance += " ❗️ (انتهى الاشتراك)"
                elif remaining_days <= 5:
                    balance += f" ⚠️ ({remaining_days} أيام متبقية)"
            table_data.append(
                [num, balance,
                 info.get('تاريخ الانتهاء', 'غير متوفر')])
        else:
            table_data.append([num, "❌ فشل", "❌ فشل"])

    headers = ["📱 الرقم", "💵 الرصيد", "📅 الانتهاء"]
    table = tabulate(table_data, headers=headers, tablefmt="grid")

    response = ("⏰ *نتائج الاستعلام التلقائي*\n"
                "══════════════════════════\n"
                f"
\n{table}\n
\n"
                f"🔢 *عدد الأرقام:* {len(user_numbers)}\n\n"
                "✨ تم الانتهاء من عملية الاستعلام بنجاح")

    await processing_msg.delete()
    await context.bot.send_message(job.chat_id,
                                   response,
                                   parse_mode='Markdown')


async def error_handler(update: Update,
                        context: ContextTypes.DEFAULT_TYPE) -> None:
    logger.error(msg="حدث خطأ في البوت:", exc_info=context.error)
    if update.message:
        await update.message.reply_text(
            "⛔ *عذراً، حدث خطأ غير متوقع*\n"
            "══════════════════════════\n"
            "لقد واجهنا مشكلة تقنية أثناء معالجة طلبك\n\n"
            "🔄 يرجى المحاولة مرة أخرى لاحقاً",
            parse_mode='Markdown')


def main():
    server_thread = threading.Thread(target=run)
    server_thread.start()

    application = ApplicationBuilder().token(
        "7563226990:AAFg2E5-f6EcVEjBK7ph5w8Q064P4daZlj0").build()

    conv_handler = ConversationHandler(
        entry_points=[CallbackQueryHandler(button_handler)],
        states={
            ADD_NUMBER:
            [MessageHandler(filters.TEXT & ~filters.COMMAND, add_number)],
            DELETE_NUMBER: [CallbackQueryHandler(delete_number)],
            SCHEDULE_HOUR: [CallbackQueryHandler(schedule_hour)]
        },
        fallbacks=[CommandHandler("start", start)],
        per_message=True)

    application.add_handler(CommandHandler("start", start))
    application.add_handler(conv_handler)
    application.add_handler(
        MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
    application.add_error_handler(error_handler)

    logger.info("🤖 البوت يعمل...")
    application.run_polling()


if __name__ == '__main__':
    main()
ASSISTANT
نعم، الكود الذي أرفقته بشكل عام جاهز للعمل مع خدمة مثل https://pulsetic.com/uptimerobot-alternative/ طالما أنها تدعم تشغيل بوتات Python مع إمكانية فتح خدمة ويب (وهو ما قمت بتوفيره عبر Flask على المنفذ 8080).

بعض النقاط المهمة للتأكد منها لكي يعمل بوتك بشكل صحيح في بيئة مثل pulsetic أو أي خدمة استضافة مشابهة:

1. **الخدمة تعتمد على بقاء البرنامج شغّالًا (Persistent Running Process):**  
   هذا ضروري لأن بوت تلغرام يعمل كعملية مستمرة (long polling). الكود يستخدم `application.run_polling()` مع Flask يخدم على الويب في خيط منفصل.

2. **المنفذ 8080 مفتوح ومسموح به في الخدمة:**  
   لأن Flask مقدم خدمة ويب على هذا المنفذ (`app.run(host="0.0.0.0", port=8080)`) ليتمكن pulsetic من ping-ها على `/` endpoint للتأكد من أن البوت يعمل (خاصية الـ Uptime Monitoring).

3. **مكتبات البايثون اللازمة مثبتة:**  
   مثل `python-telegram-bot`, `requests`, `beautifulsoup4`, `flask`, `pytz`, و `tabulate`  
   (يمكنك إضافتهم ضمن ملف `requirements.txt` في بيئة الاستضافة الخاصة بك).

4. **توكن البوت صالح ومفعّل:**  
   أي توكن البوت الذي تضعه في  
   ```python
   application = ApplicationBuilder().token(
       "7563226990:AAFg2E5-f6EcVEjBK7ph5w8Q064P4daZlj0").build()
   ```  
   يجب أن يكون مدخل صحيح.

5. **التعامل مع الملفات (json) مسموح وذات صلاحيات كتابة:**  
   نظرًا لأن الكود يحفظ ويقرأ من ملفات `saved_numbers.json` و`scheduled_times.json` في مجلد العمل الحالي، تأكد أنه يمكن للطور تنفيذ عمليات القراءة والكتابة على الملفات على الخادم.

---

### ملحوظة:

- البنية الحالية تستخدم **long polling** وعند استضافة في سيرفرات تقدم الـ webhook فقد تحتاج إلى تعديل ليعمل Webhook بدل polling، لكن معظم خدمات الاستضافة التي تسمح لك بتشغيل سيرفر Python منها بدون مشاكل **polling** تعمل بشكل جيد.

- تأكد من أن عملية بوت تلغرام تعمل كعملية دائمة ولا تنقطع — وإذا كنت تستخدم pulsetic فهذا يناسب تمامًا.

---

### خلاصة:

- **الكود جاهز للاستخدام في pulsetic** طالما تم توفير متطلبات تشغيل البيئة (مكتبات، صلاحيات الملفات).
- يحتفظ بخدمة ويب خفيفة على `/` لمراقبة uptime وهو مطلوب في pulsetic لتحديد ما إذا كان البوت ما زال قيد العمل.

---

إذا أردت، يمكنني مساعدتك في كتابة ملف `requirements.txt` أو تقديم تعليمات أخرى لتهيئة البوت في تلك المنصة. فقط أخبرني!

turns-00079.parquet:38296

063c5b68dd0ef6760f804fb4
turn 4/4gpt-4.1-mini-2025-04-14ArabicYemen255 words
degenerate_repetitionAbsentFinal dense release
USER
ماهو الرابط الذي اقوم بتقديمة لموقع
https://pulsetic.com/uptimerobot-alternative/
ASSISTANT
في حالة بوت Telegram الذي تستخدمه مع كودك الحالي، فإنك فتحت خادم ويب صغير عبر Flask على المنفذ 8080، والذي يقدم رد ثابت على مسار `/`:

```python
@app.route('/')
def home():
    return "Bot is alive!"
```

---

### إذًا:

- **الرابط الذي ستقدمه لـ pulsetic (أو أي خدمة مراقبة uptime)** هو الرابط الكامل إلى سيرفرك مع المنفذ 8080 والمسار `/`، مثلاً:

```
http://YOUR_SERVER_IP_OR_DOMAIN:8080/
```

---

### أمثلة:

- لو استضفت بوتك في سيرفر له IP مثل: `123.45.67.89`، فإن الرابط هو:  
  `http://123.45.67.89:8080/`

- لو لديك اسم نطاق (Domain) مرتبط بالسيرفر مثل: `mybot.example.com`، استخدم:  
  `http://mybot.example.com:8080/`

---

### ملاحظات مهمة:

1. **التأكد من أن المنفذ 8080 مفتوح ومُسموح بالوصول عبر الشبكة** (بما في ذلك جدار الحماية للـ VPS أو مزود الاستضافة).

2. **إذا كان مزود الاستضافة يمنع الوصول إلى المنافذ غير الشائعة**، قد تحتاج لاستخدام منفذ 80 (HTTP القياسي) أو 443 (HTTPS)، وسيستلزم تعديل:

```python
def run():
    app.run(host="0.0.0.0", port=80)
```

وتعديل إعدادات سيرفرك accordingly.

3. **إذا كنت تستخدم خدمة مثل pulsetic بدل سيرفر فعلي (مجانية محدودة مثلاً) ولا يسمحون بوصل المنفذ 8080** فقد تحتاج إلى تضمين خادم الويب على المنفذ 80 أو 443.

---

### خلاصة:

- **رابط المونيتور في pulsetic هو:**  
  `http://<your-server-ip-or-domain>:8080/`

- هذا الرابط يجب أن يعيد نص `"Bot is alive!"` تأكيدًا على أن البوت يعمل.

---

إذا كنت ستستخدم خدمة استضافة (مثل Heroku أو غيرها) يختلف الرابط حسب نطاق الاستضافة، لكن مبدأ عرض الصفحة `/` على المنفذ المستخدم هو نفسه.

---

إذا أردت مساعدة في ضبط الرابط أو ضبط المنفذ بما يناسب بيئتك، شاركني نوع الاستضافة وسأساعدك بخطوات تفصيلية.

turns-00079.parquet:38297

63650dd97169c16fe03e45d1
turn 1/1gpt-4.1-mini-2025-04-14EnglishSouth Korea23 words
degenerate_repetitionAbsentFinal dense release
USER
User: hi, how are you?
User: hi, how are you?
Assistant:
ASSISTANT
Hello! I'm doing well, thank you. How can I assist you today?

turns-00079.parquet:38298

7251fdad2d63fd003fea4aa9
turn 1/1gpt-4.1-mini-2025-04-14IndonesianVietnam98 words
degenerate_repetitionAbsentFinal dense release
USER
User: hewan yang melompat dan memanjet
User: hewan yang melompat dan memanjet
Assistant:
ASSISTANT
Hewan yang dapat melompat dan memanjat antara lain:

1. Kucing – terkenal karena kemampuannya melompat jauh dan memanjat pohon atau dinding.
2. Kelinci – ahli dalam melompat jauh, meskipun kemampuannya memanjat terbatas.
3. Tupai – sangat gesit dalam melompat dari satu cabang ke cabang lain dan memanjat pohon dengan mudah.
4. Katak – hebat dalam melompat, meskipun kemampuan memanjatnya tergantung spesies.
5. Monkeys (Monyet) – selain memanjat, mereka juga bisa melompat dari pohon ke pohon.

Apakah ada hewan tertentu yang ingin kamu ketahui lebih detail?