AbhiCalls 2.9.9__tar.gz → 2026.2.2__tar.gz
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-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/__init__.py +1 -1
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/_engine/native.py +17 -1
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/controller.py +22 -3
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/player.py +2 -1
- abhicalls-2026.2.2/AbhiCalls/plugins/base.py +93 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls.egg-info/PKG-INFO +1 -1
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/PKG-INFO +1 -1
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/pyproject.toml +1 -1
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/setup.py +1 -1
- abhicalls-2.9.9/AbhiCalls/plugins/base.py +0 -57
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/Start.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/_engine/__init__.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/models.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/plugins/__init__.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/queue.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/runtime.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/vc.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls/yt.py +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls.egg-info/SOURCES.txt +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls.egg-info/dependency_links.txt +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls.egg-info/requires.txt +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/AbhiCalls.egg-info/top_level.txt +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/README.md +0 -0
- {abhicalls-2.9.9 → abhicalls-2026.2.2}/setup.cfg +0 -0
|
@@ -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
|
|
|
@@ -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
|
-
#
|
|
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
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
QUEUE_DELETE_AFTER = 30 # seconds
|
|
5
|
+
VC_END_DELETE_AFTER = 10 # seconds
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Plugin:
|
|
9
|
+
name = "base"
|
|
10
|
+
|
|
11
|
+
def __init__(self, app):
|
|
12
|
+
self.app = app
|
|
13
|
+
self.now_playing_msg = {} # chat_id -> message
|
|
14
|
+
|
|
15
|
+
async def on_song_start(self, chat_id, song):
|
|
16
|
+
caption = (
|
|
17
|
+
"▶️ Nᴏᴡ Pʟᴀʏɪɴɢ\n\n"
|
|
18
|
+
f"🎵 Tɪᴛʟᴇ : [{song.title[:19]}]({song.url})\n"
|
|
19
|
+
f"⏱ Dᴜʀᴀᴛɪᴏɴ : {song.duration}\n"
|
|
20
|
+
f"🙋 Rᴇǫᴜᴇsᴛᴇᴅ Bʏ : {song.requested_by}"
|
|
21
|
+
)
|
|
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
|
+
|
|
31
|
+
if song.thumb:
|
|
32
|
+
msg = await self.app.send_photo(
|
|
33
|
+
chat_id,
|
|
34
|
+
photo=song.thumb,
|
|
35
|
+
caption=caption
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
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
|
|
42
|
+
|
|
43
|
+
async def on_song_end(self, chat_id, song):
|
|
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
|
|
51
|
+
|
|
52
|
+
async def on_queue_add(self, chat_id, song, position):
|
|
53
|
+
if position == 1:
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
caption = (
|
|
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ᴛᴇᴅ Bʏ : {song.requested_by}\n"
|
|
61
|
+
f"📍 Pᴏsɪᴛɪᴏɴ : {position}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if song.thumb:
|
|
65
|
+
msg = await self.app.send_photo(
|
|
66
|
+
chat_id,
|
|
67
|
+
photo=song.thumb,
|
|
68
|
+
caption=caption
|
|
69
|
+
)
|
|
70
|
+
else:
|
|
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
|
+
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="AbhiCalls",
|
|
5
|
-
version="2.
|
|
5
|
+
version="2026.2.2",
|
|
6
6
|
description="Telegram Voice Chat Music Engine with Queue, Loop, ETA support",
|
|
7
7
|
long_description=open("README.md", encoding="utf-8").read(),
|
|
8
8
|
long_description_content_type="text/markdown",
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
class Plugin:
|
|
2
|
-
name = "base"
|
|
3
|
-
|
|
4
|
-
def __init__(self, app):
|
|
5
|
-
self.app = app # pyrogram Client
|
|
6
|
-
|
|
7
|
-
# =========================
|
|
8
|
-
# ▶️ NOW PLAYING / AUTO-SKIP
|
|
9
|
-
# =========================
|
|
10
|
-
async def on_song_start(self, chat_id, song):
|
|
11
|
-
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}"
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
if song.thumb:
|
|
19
|
-
await self.app.send_photo(
|
|
20
|
-
chat_id,
|
|
21
|
-
photo=song.thumb,
|
|
22
|
-
caption=caption
|
|
23
|
-
)
|
|
24
|
-
else:
|
|
25
|
-
await self.app.send_message(chat_id, caption)
|
|
26
|
-
|
|
27
|
-
# =========================
|
|
28
|
-
# ⏹ SONG END (optional hook)
|
|
29
|
-
# =========================
|
|
30
|
-
async def on_song_end(self, chat_id, song):
|
|
31
|
-
# Usually no message needed here
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
# =========================
|
|
35
|
-
# ➕ ADDED TO QUEUE
|
|
36
|
-
# =========================
|
|
37
|
-
async def on_queue_add(self, chat_id, song, position):
|
|
38
|
-
# First song ka message already on_song_start bhej dega
|
|
39
|
-
if position == 1:
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
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}"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
if song.thumb:
|
|
51
|
-
await self.app.send_photo(
|
|
52
|
-
chat_id,
|
|
53
|
-
photo=song.thumb,
|
|
54
|
-
caption=caption
|
|
55
|
-
)
|
|
56
|
-
else:
|
|
57
|
-
await self.app.send_message(chat_id, caption)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|