AbhiCalls 2.9.9__py3-none-any.whl → 2026.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
AbhiCalls/__init__.py CHANGED
@@ -5,7 +5,7 @@ from .plugins import Plugin
5
5
 
6
6
  __all__ = ["VoiceEngine", "idle"]
7
7
 
8
- __version__ = "2.9.9"
8
+ __version__ = "2026.2.2"
9
9
  __author__ = "ABHISHEK THAKUR"
10
10
 
11
11
  try:
@@ -1,16 +1,32 @@
1
1
  from pytgcalls import PyTgCalls, filters
2
- from pytgcalls.types import MediaStream, AudioQuality, StreamEnded
2
+ from pytgcalls.types import MediaStream, AudioQuality, StreamEnded, ChatUpdate
3
+
3
4
 
4
5
  class _NativeEngine:
5
6
  def __init__(self, app):
6
7
  self._core = PyTgCalls(app)
7
8
  self.on_end = None
9
+ self.on_vc_closed = None
8
10
 
11
+ # 🔹 STREAM END
9
12
  @self._core.on_update(filters.stream_end())
10
13
  async def _ended(_, update: StreamEnded):
11
14
  if self.on_end:
12
15
  await self.on_end(update.chat_id)
13
16
 
17
+ # 🔹 VC CLOSED
18
+ @self._core.on_update(
19
+ filters.chat_update(ChatUpdate.Status.CLOSED_VOICE_CHAT)
20
+ )
21
+ async def _vc_closed(_, update):
22
+ chat_id = update.chat_id
23
+
24
+ if self.on_vc_closed:
25
+ try:
26
+ await self.on_vc_closed(chat_id)
27
+ except Exception as e:
28
+ print("[NativeEngine] on_vc_closed error:", e)
29
+
14
30
  async def start(self):
15
31
  await self._core.start()
16
32
 
AbhiCalls/controller.py CHANGED
@@ -7,7 +7,11 @@ class VoiceController:
7
7
  def __init__(self, engine):
8
8
  self.engine = engine
9
9
  self.player = Player(engine)
10
+
11
+ # engine hooks
10
12
  self.engine.on_end = self._on_end
13
+ self.engine.on_vc_closed = self._on_vc_closed
14
+
11
15
  self.plugins = []
12
16
 
13
17
  def load_plugin(self, plugin):
@@ -52,7 +56,7 @@ class VoiceController:
52
56
  first_pos = pos
53
57
  last_song = song
54
58
 
55
- # 🔥 First song actually started
59
+ # first song actually started
56
60
  if first_pos == 1 and last_song:
57
61
  await self._hook("on_song_start", chat_id, last_song)
58
62
 
@@ -97,12 +101,10 @@ class VoiceController:
97
101
  async def _on_end(self, chat_id):
98
102
  q = self.player.queues.get(chat_id)
99
103
 
100
- # old song end hook
101
104
  old_song = q.current() if q else None
102
105
  if old_song:
103
106
  await self._hook("on_song_end", chat_id, old_song)
104
107
 
105
- # move to next song
106
108
  await self.player.skip(chat_id)
107
109
 
108
110
  q = self.player.queues.get(chat_id)
@@ -110,3 +112,20 @@ class VoiceController:
110
112
 
111
113
  if next_song:
112
114
  await self._hook("on_song_start", chat_id, next_song)
115
+
116
+ # =========================
117
+ # VC CLOSED (CLEANUP)
118
+ # =========================
119
+ async def _on_vc_closed(self, chat_id):
120
+ q = self.player.queues.get(chat_id)
121
+ if q:
122
+ q.clear()
123
+
124
+ try:
125
+ await self.player.stop(chat_id)
126
+ except:
127
+ pass
128
+
129
+ await self._hook("on_vc_closed", chat_id)
130
+ print(f"[VoiceController] VC closed → cleaned {chat_id}")
131
+
AbhiCalls/player.py CHANGED
@@ -80,4 +80,5 @@ class Player:
80
80
  return None
81
81
  elapsed = int(time.time() - self.start_time.get(chat_id, 0))
82
82
  return max(q.current().duration_sec - elapsed, 0)
83
-
83
+
84
+
AbhiCalls/plugins/base.py CHANGED
@@ -1,57 +1,93 @@
1
+ import asyncio
2
+
3
+
4
+ QUEUE_DELETE_AFTER = 30 # seconds
5
+ VC_END_DELETE_AFTER = 10 # seconds
6
+
7
+
1
8
  class Plugin:
2
9
  name = "base"
3
10
 
4
11
  def __init__(self, app):
5
- self.app = app # pyrogram Client
12
+ self.app = app
13
+ self.now_playing_msg = {} # chat_id -> message
6
14
 
7
- # =========================
8
- # ▶️ NOW PLAYING / AUTO-SKIP
9
- # =========================
10
15
  async def on_song_start(self, chat_id, song):
11
16
  caption = (
12
- "▶️ **Now Playing**\n\n"
13
- f"🎵 **Title:** {song.title}\n"
14
- f"⏱ **Duration:** {song.duration}\n"
15
- f"🙋 **Requested by:** {song.requested_by}"
17
+ "▶️ Nᴏᴡ Pʟᴀʏɪɴɢ\n\n"
18
+ f"🎵 Tɪᴛʟᴇ : [{song.title[:19]}]({song.url})\n"
19
+ f"⏱ Dᴜʀᴀᴛɪᴏɴ : {song.duration}\n"
20
+ f"🙋 Rᴇǫᴜᴇsᴛᴇᴅ : {song.requested_by}"
16
21
  )
17
22
 
23
+ # delete old now-playing msg if exists
24
+ old = self.now_playing_msg.get(chat_id)
25
+ if old:
26
+ try:
27
+ await old.delete()
28
+ except:
29
+ pass
30
+
18
31
  if song.thumb:
19
- await self.app.send_photo(
32
+ msg = await self.app.send_photo(
20
33
  chat_id,
21
34
  photo=song.thumb,
22
35
  caption=caption
23
36
  )
24
37
  else:
25
- await self.app.send_message(chat_id, caption)
38
+ msg = await self.app.send_message(chat_id, caption)
39
+
40
+ # store current now playing msg
41
+ self.now_playing_msg[chat_id] = msg
26
42
 
27
- # =========================
28
- # ⏹ SONG END (optional hook)
29
- # =========================
30
43
  async def on_song_end(self, chat_id, song):
31
- # Usually no message needed here
32
- pass
44
+ # delete current song msg when song ends
45
+ msg = self.now_playing_msg.pop(chat_id, None)
46
+ if msg:
47
+ try:
48
+ await msg.delete()
49
+ except:
50
+ pass
33
51
 
34
- # =========================
35
- # ➕ ADDED TO QUEUE
36
- # =========================
37
52
  async def on_queue_add(self, chat_id, song, position):
38
- # First song ka message already on_song_start bhej dega
39
53
  if position == 1:
40
54
  return
41
55
 
42
56
  caption = (
43
- "➕ **Added to Queue**\n\n"
44
- f"🎵 **Title:** {song.title}\n"
45
- f"⏱ **Duration:** {song.duration}\n"
46
- f"🙋 **Requested by:** {song.requested_by}\n"
47
- f"📍 **Position:** {position}"
57
+ "➕ Aᴅᴅᴇᴅ Tᴏ Qᴜᴇᴜᴇ\n\n"
58
+ f"🎵 Tɪᴛʟᴇ : [{song.title[:19]}]({song.url})\n"
59
+ f"⏱ Dᴜʀᴀᴛɪᴏɴ : {song.duration}\n"
60
+ f"🙋 Rᴇǫᴜᴇsᴛᴇᴅ : {song.requested_by}\n"
61
+ f"📍 Pᴏsɪᴛɪᴏɴ : {position}"
48
62
  )
49
63
 
50
64
  if song.thumb:
51
- await self.app.send_photo(
65
+ msg = await self.app.send_photo(
52
66
  chat_id,
53
67
  photo=song.thumb,
54
68
  caption=caption
55
69
  )
56
70
  else:
57
- await self.app.send_message(chat_id, caption)
71
+ msg = await self.app.send_message(chat_id, caption)
72
+
73
+ # auto delete queue msg after 30 sec
74
+ asyncio.create_task(self._auto_delete(msg, QUEUE_DELETE_AFTER))
75
+
76
+ async def on_vc_closed(self, chat_id):
77
+ try:
78
+ msg = await self.app.send_message(
79
+ chat_id,
80
+ "🔴 Vᴏɪᴄᴇ Cʜᴀᴛ Eɴᴅᴇᴅ\n\n"
81
+ "🧹 Qᴜᴇᴜᴇ Cʟᴇᴀʀᴇᴅ & Pʟᴀʏᴇʀ Sᴛᴏᴘᴘᴇᴅ."
82
+ )
83
+ asyncio.create_task(self._auto_delete(msg, VC_END_DELETE_AFTER))
84
+ except:
85
+ pass
86
+
87
+ async def _auto_delete(self, msg, delay):
88
+ try:
89
+ await asyncio.sleep(delay)
90
+ await msg.delete()
91
+ except:
92
+ pass
93
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbhiCalls
3
- Version: 2.9.9
3
+ Version: 2026.2.2
4
4
  Summary: Telegram Voice Chat Music Engine with Queue, Loop, ETA and Player controls
5
5
  Home-page: https://github.com/YouTubeMusicAPI/TgCall
6
6
  Author: Abhishek Thakur
@@ -0,0 +1,17 @@
1
+ AbhiCalls/Start.py,sha256=Z4Kn3p8elM9Sgo3H_7EBP8dfHknsjgiaUaEs1GEqcnY,1068
2
+ AbhiCalls/__init__.py,sha256=qClYd7guH3WOXqKuTIpNWVS3PYZvNIy68k-UgrmkUSc,325
3
+ AbhiCalls/controller.py,sha256=5fBN8qIqiaCuIOsoLqh_rJz33_Zqtrf5nlm27mMD-hk,3685
4
+ AbhiCalls/models.py,sha256=fwMdHKE0z6qB98nHJ8E1uYU3JSk25LGxaaeAz1JkAH0,791
5
+ AbhiCalls/player.py,sha256=85_kitFEh7N65q9hWB6lnFRAz_ZpDctpGfTsA_-6m54,2285
6
+ AbhiCalls/queue.py,sha256=iRrTvt1rU-diarSBCfxjeY-4uJ97oUsvDqtPpbB4UQY,1212
7
+ AbhiCalls/runtime.py,sha256=4_WmHirGwNOyn1Iuzbhecb_ao7qMDh1erFzp0ZaLze0,966
8
+ AbhiCalls/vc.py,sha256=ld3C4-QNDrHBQPrNzIuNTcu7zk1pVOo9rvrQBUsTI8s,309
9
+ AbhiCalls/yt.py,sha256=h0QRNK9sy-5wIDueiy-uMUxruV7z5m3xjUyRPoFT6_s,2594
10
+ AbhiCalls/_engine/__init__.py,sha256=pTqDs11-vYr-wuwtd7h3EkspDquGu84jWfN54ajl0kM,34
11
+ AbhiCalls/_engine/native.py,sha256=7Lo8SFidlXzOUXl9OtPYDMKi6e81rX2G6Kt18gfSh0Q,1773
12
+ AbhiCalls/plugins/__init__.py,sha256=rSCdGAjFfrzz-XQIFRjOcl629mPCo17n77Fn9lxLcPI,25
13
+ AbhiCalls/plugins/base.py,sha256=sGw4bahjD89ct88x2khSNRefclHhLIAnumXdQ9BjmHw,2774
14
+ abhicalls-2026.2.2.dist-info/METADATA,sha256=U4UlbTyfGf86q5lZUZMJAGuRdWusz75QC23UPqEhtTE,1212
15
+ abhicalls-2026.2.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
16
+ abhicalls-2026.2.2.dist-info/top_level.txt,sha256=3_ZgJEaE0rS9jpmEMi6oIXEtMzIagrn70XtK7H6w2gA,10
17
+ abhicalls-2026.2.2.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- AbhiCalls/Start.py,sha256=Z4Kn3p8elM9Sgo3H_7EBP8dfHknsjgiaUaEs1GEqcnY,1068
2
- AbhiCalls/__init__.py,sha256=uoXjElr_JmTjFdMHzNKBQbNzwGz4m1j9r2ChxrXouAE,322
3
- AbhiCalls/controller.py,sha256=KYppQPpWhTpWj-gwmZqyGgX5liZHAaWRIMqksZPzXzU,3241
4
- AbhiCalls/models.py,sha256=fwMdHKE0z6qB98nHJ8E1uYU3JSk25LGxaaeAz1JkAH0,791
5
- AbhiCalls/player.py,sha256=UlAWmhZqHFjtAplA9-6lXFkVAu4pyEWDGbF8Wj9KYkM,2286
6
- AbhiCalls/queue.py,sha256=iRrTvt1rU-diarSBCfxjeY-4uJ97oUsvDqtPpbB4UQY,1212
7
- AbhiCalls/runtime.py,sha256=4_WmHirGwNOyn1Iuzbhecb_ao7qMDh1erFzp0ZaLze0,966
8
- AbhiCalls/vc.py,sha256=ld3C4-QNDrHBQPrNzIuNTcu7zk1pVOo9rvrQBUsTI8s,309
9
- AbhiCalls/yt.py,sha256=h0QRNK9sy-5wIDueiy-uMUxruV7z5m3xjUyRPoFT6_s,2594
10
- AbhiCalls/_engine/__init__.py,sha256=pTqDs11-vYr-wuwtd7h3EkspDquGu84jWfN54ajl0kM,34
11
- AbhiCalls/_engine/native.py,sha256=KJajnL9urwrx0PaOEJj4-pGGnhz4X9FDI6rTGrbCLoI,1272
12
- AbhiCalls/plugins/__init__.py,sha256=rSCdGAjFfrzz-XQIFRjOcl629mPCo17n77Fn9lxLcPI,25
13
- AbhiCalls/plugins/base.py,sha256=wanPA2h4dHvR-YXGIsAgkAGGtGMd6acT0Gg8q35RTgI,1696
14
- abhicalls-2.9.9.dist-info/METADATA,sha256=3iVxiggOeLOrWg6msDJOCW3DCI8kmfuYRITyb9fKdhI,1209
15
- abhicalls-2.9.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
16
- abhicalls-2.9.9.dist-info/top_level.txt,sha256=3_ZgJEaE0rS9jpmEMi6oIXEtMzIagrn70XtK7H6w2gA,10
17
- abhicalls-2.9.9.dist-info/RECORD,,