Y1jVY3iA#o@wftrk-i)nWmm;>Y1jVY3iA#o@wgY4OVnY
zH%sqkS>0d)Lcj#YA)?l(hqdTo%pS(Y2)
z*p-qYYDW(JoY2)4*i}(zvs~JIrMuD{hmX==g{vt^m`7j
z`&x?nJ%@hJq2F`p_Z<2?hknn|**fB=-*a>&2dI9}(X}$5`aOq!&%t$HW%YZGsNZww
z_Z<2?hknnY-*f2q9Qr*6*L{tte$S!bb42}~qt|_)`aMU~?>Y2)j$Zd6tKW0z_Z<2?
zN3Z(`QNQQV?>Y2)4zB(hqJGbz-*f2q9Qr+ne$S!bbLjUR`aOq!&!OLQ==U7@J%@hJ
zq2F`p_Z<2?hknnY-y0&YhRCZS_VN&Wd5Enp;y&>(ey=0`xx
zkARpT0Wm)U-pe@m5ivhP2v!Mz4>JU-1dy>x0K_T*5Nk(3tP%i~jV{^f>L?Vlve6|Q
zU9!<78(p%|)ln#Z#VP?1s{}yIkAPSu0P0oPMPI0_Y;?&+muz&&Mwe`K$wrrKbje1S
zY;?&+S4W{pi}?`{s{}y3{<`F$OCGv93PlK334rQ1T=LK*4_)w3!?8*L#QX?|RRSPZ
z34mB70AiH@+rr+4v?aNd_*Qtus+8art8x&ZBCpC()a9TYTlg06R^pq0$K)X5--4Km
z8uJ#$yp18Z;nz-lpY15}znvksBP0r0d-F|{?_i#HFwZ+g^Sp~8cQNEHhP+j7U-%Th
z2yiFHByXqucFK3N^m~BEvDb$7{uJ=Iyr1|1Mcl_Z6Z>!q@8}rQ6WrG!F2z|8ZsD1#
z9W}WDd&ONs88QE5<{tC8n!Mbsl0H01ED_vy
z#q-3YHCfCGymAzTTjr%!v?e#0i>%c(IbzmZ`)YEdS!LzL}qFc#~ZHOJpSH@$z
za^+m$3nFZYEf&_^7Gq|$w3R`i^)s=pT{~l$%7wA@b}(WyU-R)VNz~abspX;!hwnhPMs}Aj5A0
zD@d!ky-?T|{8C^py)3POxY-v-8^gQKF}w#*ul0-R`?^_sqz4ufc`JZfLxIs?#Q6tz5rHYu@{f*3)ab8M}kMXQiUs9TJ!t}QxX
zdQBgW{pe4PTc2|uTkTX1+FvhJGp$|BENb@ra$_}pGv*-l%cEB^;6;IYF7|$g<#u7O
zxEOo(LL8M<6=-!K?tI2k=PcPaiLf$C)YdKFSVZU;e(wig`u<;OEP61raJ7E@`7i%j
z%Mm7c6RH8vjW@y*HQ~AMrFbs81^#atp20W`&x@RaXJKCg-*^_DeprdO^>MEp?>enE
zuQ0EKSB>Fms&lbD&l)_NwibL{2Rm7hUB)(meQnTPnXw5B=`gRwebXd%lue=5ufq!d
z>&->x4d!Cg1(u}oYi3yfp*)4o*cy9ZF{iK*Isj_*#}>K
zHNH>bm^Ydswt1F)KT3PeV>kB$Xuk>cxLWeHXvH#Gwu)XlWUhnV9x>OO8_ZF2qq)f(
zGdJT47q^<*u-Da_%$v;}=1%h#a~F0VejA=Qy~n)6{I$6kd#XNQ-fO0?Z`_0CA@i{L
z0PO0+<|F2#Sgrkg^D$g2K5jl?K572Ze9BClPn#L@S@Tck^VrAk3+7Aa%h<#9E7)c8
zYvxh&b@MNHtK=Kzo7gMZ$G4cjZN6h3Gyh>8H{UbgH&2)!iiM|K|J(fB{KEVa`{w=1
z{Ezvyc^Y3>`z_wL|DE|gzWMhD^AqfV_EYmST)W5tr1=5f)ql$T7*BjON>rM#3CdDw#@f)lwBXf_WwKmOlhe(D
z7|hvM$V=o*%*9`d+4RfgY*{5Qm(}tLyy16_#PEp9x$;-oefB&NS&Q8a*5N^y_3|3o
zAQzZt%(M7HeOxw5yKKS-5<2jOt<93e2kTREp}bCPc|B&$Z;*>I-`s+k;U$=>ZIvGE
zan*~b)-RVne4uT+^yA6$ow7>?@R;;&>_~Hk?3F9A>&8`bwOoTa#-Ged>{vD=85x$W
zjNntcqcSGr_*U+IIe;CWCNTOb%A{P2kxv=lH^gWMpH93^4&(iw>+wa0qjIC%gi+4T
z7}wm2k<9HFwY*vGkUQlq7^l2d-iGnW-Exn-L*9wkS>C0i5V;R~5560(kiSQc%LDRW
zc^^h4C*(nSNFK(>v)g#U*#L}O?*ev$G5${jqgi;M;^m2
zhL6j4<$Lmdc>+7ZJt;rH{slk6qdh;CpU8j8e_?m&pUThV=lF=?FR}CTudti;ujOg^
zjrx(A3rP4$)Dt;%*nhgSa>nXvaALxVl`S(s|gP%Eyb$@EmkYu
zGFon(W}V)gElx~ivbaQ7nzjw%>N(~{x~rvPWZy`!5;+2h?#pDWm0a`)4^3Ugv7(zh
z(3A;|(e1&nZg3a~4&@+jcP(zN7Wa~Drc`vJBf+6NIOKxE_Tbu_+>VodbvMCcBqg}(L+`(Kl!$V6tn=fUn6QhOPbuHONxjBt$1$A!DhGO&*
z)qZv(dhwvkN@5_SMHL)SX28g6=Eu5b1*!kpiG~
zdtIbfx9+6H%}f@{l~QqXJlD|ejy2@a)%~@)7Hf6w59(UvVOjrp)g8-}suP7wwX&>O
ze-_;p=vfN%+*MalYpL#}Wni%hTFQ%Z>p)$k)^h#HVEsv@?j$m(Jyy{k+a2^+HR!S3
zwOUkbwb&ihq8ijOKrOr@Fosu?f45ju^+7wD1q4e5N+8jz@&YQxVH-$MT!kiOf&WSMRM5tdP
zEF%$?kqFC3gk>bcG7@1K9bp+AVHq7^869C69bp+AVHq7^869C69bp+AVHuml6q~~o
zo5K{F!xWpt6vZBhB+t0oReYB$uQ^6Fz3#&zMWwionaZBVHura
z8J%GnonaZBVHura8L6<0R9HqTEF%?`kqXO5g=M6|GE!j~$@ZqIo8Op7b~jdvZn3;<
zB%dqg%K0+Sn!5^<;~9!eGj6exE9CN-*6vBn@-Xey_)EGgwcmT;)EU&;Katm~LU6LX
zE^>4KL~bmIbVeT0pI25R%ZhBtRWgk|8C(^cYUM=sA*cc6G!BkKifBQNmuDs?GjM?u
z!y_4MTh-cLwf5%Wq*$`GBj2!Vyx2I9ADhTD3}&iLH7y!;jOQED_}fv=w`xA8G&rpm
z;naC^CaidCuC4}4pg_mEM%o-1{N`;_GZq>Rj-&73h(@+{OMlm*+$h0@|&`?30YUMUeqCQ#tL5;?uHgKzE
zcS@bp{!G2rTAo4g)PCeiGZRy67#+>yuV9UiTBG|R?YBmUaTvBn$8i|9Mi1a{V9AkO
zsn}L5k2YaU!eiwS&zDxlaiP+XC8Nb^DG>4pgE-~($cqE7SFcJb2#fo$wE}!
ZRF|my2H}F)C!P++aWVWhoQb*f{{UOt9vA=s
literal 0
HcmV?d00001
diff --git a/logger.py b/logger.py
index 7b4fedf5..e635fffb 100644
--- a/logger.py
+++ b/logger.py
@@ -17,7 +17,7 @@
import logging
logging.basicConfig(
- level=logging.WARNING,
+ level=logging.INFO,
format="[%(asctime)s - %(levelname)s] - %(name)s - %(message)s",
datefmt='%d-%b-%y %H:%M:%S',
handlers=[
@@ -29,8 +29,10 @@
logging.StreamHandler()
]
)
-logging.getLogger("pyrogram").setLevel(logging.WARNING)
-logging.getLogger("pytgcalls").setLevel(logging.WARNING)
+
+logging.getLogger("pyrogram").setLevel(logging.ERROR)
+logging.getLogger("pytgcalls").setLevel(logging.ERROR)
+logging.getLogger("apscheduler").setLevel(logging.ERROR)
LOGGER=logging.getLogger(__name__)
diff --git a/main.py b/main.py
index eca2dcf7..1a121141 100644
--- a/main.py
+++ b/main.py
@@ -12,16 +12,24 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
-from utils import start_stream
-from user import group_call
+from utils import (
+ play,
+ start_stream,
+ startup_check,
+ sync_from_db
+)
+from user import group_call, USER
from logger import LOGGER
from config import Config
from pyrogram import idle
from bot import bot
import asyncio
import os
+if Config.DATABASE_URI:
+ from database import Database
+ db = Database()
+
if not os.path.isdir("./downloads"):
os.makedirs("./downloads")
else:
@@ -31,12 +39,37 @@
async def main():
await bot.start()
Config.BOT_USERNAME = (await bot.get_me()).username
- await group_call.start()
- await start_stream()
- LOGGER.warning(f"{Config.BOT_USERNAME} Started.")
+ LOGGER.info(f"{Config.BOT_USERNAME} Started.")
+ try:
+ await group_call.start()
+ Config.USER_ID = (await USER.get_me()).id
+ if Config.DATABASE_URI:
+ if await db.is_saved("RESTART"):
+ msg=await db.get_config("RESTART")
+ if msg:
+ try:
+ k=await bot.edit_message_text(msg['chat_id'], msg['msg_id'], text="Succesfully restarted.")
+ await db.del_config("RESTART")
+ except:
+ pass
+ await sync_from_db()
+ k=await startup_check()
+ if k == False:
+ LOGGER.error("Startup checks not passed , bot is quiting")
+ await bot.stop()
+ await group_call.stop()
+ return
+ if Config.IS_LOOP:
+ if Config.playlist:
+ await play()
+ LOGGER.info("Loop play enabled and playlist is not empty, resuming playlist.")
+ else:
+ LOGGER.info("Loop play enabled , starting playing startup stream.")
+ await start_stream()
+ except Exception as e:
+ LOGGER.error(f"Startup was unsuccesfull, Errors - {e}")
+ pass
await idle()
- LOGGER.warning("Stoping")
- await group_call.start()
await bot.stop()
if __name__ == '__main__':
diff --git a/plugins/callback.py b/plugins/callback.py
index 5301ee3b..1c504532 100644
--- a/plugins/callback.py
+++ b/plugins/callback.py
@@ -13,146 +13,566 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from utils import get_admins, get_buttons, get_playlist_str, mute, pause, restart_playout, resume, seek_file, shuffle_playlist, skip, unmute
-from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
-from pyrogram.errors import MessageNotModified
+from logger import LOGGER
from pyrogram import Client
+from contextlib import suppress
from config import Config
from asyncio import sleep
-from logger import LOGGER
+import datetime
+import pytz
+import calendar
+from utils import (
+ cancel_all_schedules,
+ delete_messages,
+ get_admins,
+ get_buttons,
+ get_playlist_str,
+ leave_call,
+ mute,
+ pause,
+ recorder_settings,
+ restart,
+ restart_playout,
+ resume,
+ schedule_a_play,
+ seek_file,
+ set_config,
+ settings_panel,
+ shuffle_playlist,
+ skip,
+ start_record_stream,
+ stop_recording,
+ sync_to_db,
+ unmute,
+ volume,
+ volume_buttons
+ )
+from pyrogram.types import (
+ InlineKeyboardMarkup,
+ InlineKeyboardButton,
+ CallbackQuery
+)
+from pyrogram.errors import (
+ MessageNotModified,
+ MessageIdInvalid,
+ QueryIdInvalid
+)
+from pyrogram.types import (
+ InlineKeyboardButton,
+ InlineKeyboardMarkup
+)
+
+IST = pytz.timezone(Config.TIME_ZONE)
@Client.on_callback_query()
async def cb_handler(client: Client, query: CallbackQuery):
- admins = await get_admins(Config.CHAT)
- if query.from_user.id not in admins and query.data != "help":
- await query.answer(
- "😒 Played Joji.mp3",
- show_alert=True
- )
- return
- if query.data == "shuffle":
- if not Config.playlist:
- await query.answer("Playlist is empty.", show_alert=True)
+ with suppress(MessageIdInvalid, MessageNotModified, QueryIdInvalid):
+ admins = await get_admins(Config.CHAT)
+ if query.data.startswith("info"):
+ me, you = query.data.split("_")
+ text="Join @subin_works"
+ if you == "volume":
+ await query.answer()
+ await query.message.edit_reply_markup(reply_markup=await volume_buttons())
+ return
+ if you == "player":
+ if not Config.CALL_STATUS:
+ return await query.answer("Not Playing anything.", show_alert=True)
+ await query.message.edit_reply_markup(reply_markup=await get_buttons())
+ await query.answer()
+ return
+ if you == "video":
+ text="Toggle your bot to Video / Audio Player."
+ elif you == "shuffle":
+ text="Enable or disable auto playlist shuffling"
+ elif you == "admin":
+ text="Enable to restrict the play command only for admins."
+ elif you == "mode":
+ text="Enabling Non- stop playback will make the player running 24 / 7 and automatic startup when restarting. "
+ elif you == "title":
+ text="Enable to edit the VideoChat title to Current playing song's title."
+ elif you == "reply":
+ text="Choose whether to auto-reply messaged for userbot. "
+ elif you == "videorecord":
+ text = "Enable to record both video and audio, if disabled only audio will be recorded."
+ elif you == "videodimension":
+ text = "Choose the recording video's dimensions"
+ elif you == "rectitle":
+ text = "A custom title for your chat recordings, Use /rtitle command to set a title"
+ elif you == "recdumb":
+ text = "A channel to which all the recordings are forwarded. Make sure The User account is admin over there. Set one using /env or /config."
+ await query.answer(text=text, show_alert=True)
+
+
+ if query.data.startswith("help"):
+ if query.message.chat.type != "private" and query.message.reply_to_message.from_user is None:
+ return await query.answer("I cant help you here, since you are an anonymous admin, message me in private chat.", show_alert=True)
+ elif query.message.chat.type != "private" and query.from_user.id != query.message.reply_to_message.from_user.id:
+ return await query.answer("Okda")
+ me, nyav = query.data.split("_")
+ back=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton("Back", callback_data="help_main"),
+ InlineKeyboardButton("Close", callback_data="close"),
+ ],
+ ]
+ )
+ if nyav == 'main':
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"Play", callback_data='help_play'),
+ InlineKeyboardButton(f"Settings", callback_data=f"help_settings"),
+ InlineKeyboardButton(f"Recording", callback_data='help_record'),
+ ],
+ [
+ InlineKeyboardButton("Scheduling", callback_data="help_schedule"),
+ InlineKeyboardButton("Controling", callback_data='help_control'),
+ InlineKeyboardButton("Admins", callback_data="help_admin"),
+ ],
+ [
+ InlineKeyboardButton(f"Misc", callback_data='help_misc'),
+ InlineKeyboardButton("Config Vars", callback_data='help_env'),
+ InlineKeyboardButton("Close", callback_data="close"),
+ ],
+ ]
+ )
+ await query.message.edit("Showing help menu, Choose from the below options.", reply_markup=reply_markup, disable_web_page_preview=True)
+ elif nyav == 'play':
+ await query.message.edit(Config.PLAY_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'settings':
+ await query.message.edit(Config.SETTINGS_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'schedule':
+ await query.message.edit(Config.SCHEDULER_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'control':
+ await query.message.edit(Config.CONTROL_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'admin':
+ await query.message.edit(Config.ADMIN_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'misc':
+ await query.message.edit(Config.MISC_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'record':
+ await query.message.edit(Config.RECORDER_HELP, reply_markup=back, disable_web_page_preview=True)
+ elif nyav == 'env':
+ await query.message.edit(Config.ENV_HELP, reply_markup=back, disable_web_page_preview=True)
+
+ if not (query.from_user is None or query.from_user.id in admins):
+ await query.answer(
+ "😒 Played Joji.mp3",
+ show_alert=True
+ )
return
- await shuffle_playlist()
- await sleep(1)
- try:
- await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
-
- elif query.data.lower() == "pause":
- if Config.PAUSE:
- await query.answer("Already Paused", show_alert=True)
- else:
- await pause()
- await sleep(1)
- try:
+ #scheduler stuffs
+ if query.data.startswith("sch"):
+ if query.message.chat.type != "private" and query.message.reply_to_message.from_user is None:
+ return await query.answer("You cant use scheduling here, since you are an anonymous admin. Schedule from private chat.", show_alert=True)
+ if query.message.chat.type != "private" and query.from_user.id != query.message.reply_to_message.from_user.id:
+ return await query.answer("Okda")
+ data = query.data
+ today = datetime.datetime.now(IST)
+ smonth=today.strftime("%B")
+ obj = calendar.Calendar()
+ thisday = today.day
+ year = today.year
+ month = today.month
+ if data.startswith("sch_month"):
+ none, none , yea_r, month_, day = data.split("_")
+ if yea_r == "choose":
+ year=int(year)
+ months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
+ button=[]
+ button_=[]
+ k=0
+ for month in months:
+ k+=1
+ year_ = year
+ if k < int(today.month):
+ year_ += 1
+ button_.append([InlineKeyboardButton(text=f"{str(month)} {str(year_)}",callback_data=f"sch_showdate_{year_}_{k}")])
+ else:
+ button.append([InlineKeyboardButton(text=f"{str(month)} {str(year_)}",callback_data=f"sch_showdate_{year_}_{k}")])
+ button = button + button_
+ button.append([InlineKeyboardButton("Close", callback_data="schclose")])
+ await query.message.edit("Now Choose the month to schedule a voicechatㅤ ㅤㅤ", reply_markup=InlineKeyboardMarkup(button))
+ elif day == "none":
+ return
+ else:
+ year = int(yea_r)
+ month = int(month_)
+ date = int(day)
+ datetime_object = datetime.datetime.strptime(str(month), "%m")
+ smonth = datetime_object.strftime("%B")
+ button=[]
+ if year == today.year and month == today.month and date == today.day:
+ now = today.hour
+ else:
+ now=0
+ l = list()
+ for i in range(now, 24):
+ l.append(i)
+ splited=[l[i:i + 6] for i in range(0, len(l), 6)]
+ for i in splited:
+ k=[]
+ for d in i:
+ k.append(InlineKeyboardButton(text=f"{d}",callback_data=f"sch_day_{year}_{month}_{date}_{d}"))
+ button.append(k)
+ if month == today.month and date < today.day and year==today.year+1:
+ pyear=year-1
+ else:
+ pyear=year
+ button.append([InlineKeyboardButton("Back", callback_data=f"sch_showdate_{pyear}_{month}"), InlineKeyboardButton("Close", callback_data="schclose")])
+ await query.message.edit(f"Choose the hour of {date} {smonth} {year} to schedule a voicechat.", reply_markup=InlineKeyboardMarkup(button))
+
+ elif data.startswith("sch_day"):
+ none, none, year, month, day, hour = data.split("_")
+ year = int(year)
+ month = int(month)
+ day = int(day)
+ hour = int(hour)
+ datetime_object = datetime.datetime.strptime(str(month), "%m")
+ smonth = datetime_object.strftime("%B")
+ if year == today.year and month == today.month and day == today.day and hour == today.hour:
+ now=today.minute
+ else:
+ now=0
+ button=[]
+ l = list()
+ for i in range(now, 60):
+ l.append(i)
+ for i in range(0, len(l), 6):
+ chunk = l[i:i + 6]
+ k=[]
+ for d in chunk:
+ k.append(InlineKeyboardButton(text=f"{d}",callback_data=f"sch_minute_{year}_{month}_{day}_{hour}_{d}"))
+ button.append(k)
+ button.append([InlineKeyboardButton("Back", callback_data=f"sch_month_{year}_{month}_{day}"), InlineKeyboardButton("Close", callback_data="schclose")])
+ await query.message.edit(f"Choose minute of {hour}th hour on {day} {smonth} {year} to schedule Voicechat.", reply_markup=InlineKeyboardMarkup(button))
+
+ elif data.startswith("sch_minute"):
+ none, none, year, month, day, hour, minute = data.split("_")
+ year = int(year)
+ month = int(month)
+ day = int(day)
+ hour = int(hour)
+ minute = int(minute)
+ datetime_object = datetime.datetime.strptime(str(month), "%m")
+ smonth = datetime_object.strftime("%B")
+ if year == today.year and month == today.month and day == today.day and hour == today.hour and minute <= today.minute:
+ await query.answer("I dont have a timemachine to go to past!!!.")
+ return
+ final=f"{day}th {smonth} {year} at {hour}:{minute}"
+ button=[
+ [
+ InlineKeyboardButton("Confirm", callback_data=f"schconfirm_{year}-{month}-{day} {hour}:{minute}"),
+ InlineKeyboardButton("Back", callback_data=f"sch_day_{year}_{month}_{day}_{hour}")
+ ],
+ [
+ InlineKeyboardButton("Close", callback_data="schclose")
+ ]
+ ]
+ data=Config.SCHEDULED_STREAM.get(f"{query.message.chat.id}_{query.message.message_id}")
+ if not data:
+ await query.answer("This schedule is expired", show_alert=True)
+ if data['3'] == "telegram":
+ title=data['1']
+ else:
+ title=f"[{data['1']}]({data['2']})"
+ await query.message.edit(f"Your Stream {title} is now scheduled to start on {final}\n\nClick Confirm to confirm the time.", reply_markup=InlineKeyboardMarkup(button), disable_web_page_preview=True)
+
+ elif data.startswith("sch_showdate"):
+ tyear=year
+ none, none, year, month = data.split("_")
+ datetime_object = datetime.datetime.strptime(month, "%m")
+ thissmonth = datetime_object.strftime("%B")
+ obj = calendar.Calendar()
+ thisday = today.day
+ year = int(year)
+ month = int(month)
+ m=obj.monthdayscalendar(year, month)
+ button=[]
+ button.append([InlineKeyboardButton(text=f"{str(thissmonth)} {str(year)}",callback_data=f"sch_month_choose_none_none")])
+ days=["Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ f=[]
+ for day in days:
+ f.append(InlineKeyboardButton(text=f"{day}",callback_data=f"day_info_none"))
+ button.append(f)
+ for one in m:
+ f=[]
+ for d in one:
+ year_=year
+ if year==today.year and month == today.month and d < int(today.day):
+ year_ += 1
+ if d == 0:
+ k="\u2063"
+ d="none"
+ else:
+ k=d
+ f.append(InlineKeyboardButton(text=f"{k}",callback_data=f"sch_month_{year_}_{month}_{d}"))
+ button.append(f)
+ button.append([InlineKeyboardButton("Close", callback_data="schclose")])
+ await query.message.edit(f"Choose the day of the month you want to schedule the voicechat.\nToday is {thisday} {smonth} {tyear}. Chooosing a date preceeding today will be considered as next year {year+1}", reply_markup=InlineKeyboardMarkup(button))
+
+ elif data.startswith("schconfirm"):
+ none, date = data.split("_")
+ date = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M')
+ local_dt = IST.localize(date, is_dst=None)
+ utc_dt = local_dt.astimezone(pytz.utc).replace(tzinfo=None)
+ job_id=f"{query.message.chat.id}_{query.message.message_id}"
+ Config.SCHEDULE_LIST.append({"job_id":job_id, "date":utc_dt})
+ Config.SCHEDULE_LIST = sorted(Config.SCHEDULE_LIST, key=lambda k: k['date'])
+ await schedule_a_play(job_id, utc_dt)
+ await query.message.edit(f"Succesfully scheduled to stream on {date.strftime('%b %d %Y, %I:%M %p')}
")
+ await delete_messages([query.message, query.message.reply_to_message])
+
+ elif query.data == 'schcancelall':
+ await cancel_all_schedules()
+ await query.message.edit("All Scheduled Streams are cancelled succesfully.")
+
+ elif query.data == "schcancel":
+ buttons = [
+ [
+ InlineKeyboardButton('Yes, Iam Sure!!', callback_data='schcancelall'),
+ InlineKeyboardButton('No', callback_data='schclose'),
+ ]
+ ]
+ await query.message.edit("Are you sure that you want to cancel all the scheduled streams?", reply_markup=InlineKeyboardMarkup(buttons))
+ elif data == "schclose":
+ await query.answer("Menu Closed")
+ await query.message.delete()
+ await query.message.reply_to_message.delete()
+
+ if query.data == "shuffle":
+ if not Config.playlist:
+ await query.answer("Playlist is empty.")
+ return
+ await shuffle_playlist()
+ await query.answer("Playlist shuffled.")
+ await sleep(1)
await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
- elif query.data.lower() == "resume":
- if not Config.PAUSE:
- await query.answer("Nothing Paused to resume", show_alert=True)
- else:
- await resume()
- await sleep(1)
- try:
+
+ elif query.data.lower() == "pause":
+ if Config.PAUSE:
+ await query.answer("Already Paused")
+ else:
+ await pause()
+ await query.answer("Stream Paused")
+ await sleep(1)
+
await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
-
- elif query.data=="skip":
- if not Config.playlist:
- await query.answer("No songs in playlist", show_alert=True)
- else:
- await skip()
- await sleep(1)
- if Config.playlist:
- title=f"{Config.playlist[0][1]}"
- elif Config.STREAM_LINK:
- title=f"Stream Using [Url]({Config.DATA['FILE_DATA']['file']})"
- else:
- title=f"Streaming Startup [stream]({Config.STREAM_URL})"
-
- try:
+
+
+ elif query.data.lower() == "resume":
+ if not Config.PAUSE:
+ await query.answer("Nothing Paused to resume")
+ else:
+ await resume()
+ await query.answer("Redumed the stream")
+ await sleep(1)
+ await query.message.edit_reply_markup(reply_markup=await get_buttons())
+
+ elif query.data=="skip":
+ if not Config.playlist:
+ await query.answer("No songs in playlist")
+ else:
+ await query.answer("Trying to skip from playlist.")
+ await skip()
+ await sleep(1)
+ if Config.playlist:
+ title=f"{Config.playlist[0][1]}\nㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
+ elif Config.STREAM_LINK:
+ title=f"Stream Using [Url]({Config.DATA['FILE_DATA']['file']})ㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
+ else:
+ title=f"Streaming Startup [stream]({Config.STREAM_URL}) ㅤ ㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
await query.message.edit(f"{title}",
disable_web_page_preview=True,
reply_markup=await get_buttons()
)
- except MessageNotModified:
- pass
- elif query.data=="replay":
- if not Config.playlist:
- await query.answer("No songs in playlist", show_alert=True)
- else:
- await restart_playout()
- await sleep(1)
- try:
+
+ elif query.data=="replay":
+ if not Config.playlist:
+ await query.answer("No songs in playlist")
+ else:
+ await query.answer("trying to restart player")
+ await restart_playout()
+ await sleep(1)
await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
-
- elif query.data=="help":
- buttons = [
- [
- InlineKeyboardButton('⚙️ Update Channel', url='https://t.me/subin_works'),
- InlineKeyboardButton('🧩 Source', url='https://github.com/subinps/VCPlayerBot'),
+
+
+ elif query.data=="help":
+ buttons = [
+ [
+ InlineKeyboardButton('⚙️ Update Channel', url='https://t.me/subin_works'),
+ InlineKeyboardButton('🧩 Source', url='https://github.com/subinps/VCPlayerBot'),
+ ]
]
- ]
- reply_markup = InlineKeyboardMarkup(buttons)
- try:
+ reply_markup = InlineKeyboardMarkup(buttons)
await query.message.edit(
Config.HELP,
reply_markup=reply_markup
)
- except MessageNotModified:
- pass
- elif query.data.lower() == "mute":
- if Config.MUTED:
- await unmute()
- else:
- await mute()
- await sleep(1)
- try:
- await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
- elif query.data.lower() == 'seek':
- if not Config.CALL_STATUS:
- return await query.answer("Not Playing anything.", show_alert=True)
- if not (Config.playlist or Config.STREAM_LINK):
- return await query.answer("Startup stream cant be seeked.", show_alert=True)
- data=Config.DATA.get('FILE_DATA')
- if not data.get('dur', 0) or \
- data.get('dur') == 0:
- return await query.answer("This stream cant be seeked..", show_alert=True)
- k, reply = await seek_file(10)
- if k == False:
- return await query.answer(reply, show_alert=True)
- try:
+
+ elif query.data.lower() == "mute":
+ if Config.MUTED:
+ await unmute()
+ await query.answer("Unmuted stream")
+ else:
+ await mute()
+ await query.answer("Muted stream")
+ await sleep(1)
+ await query.message.edit_reply_markup(reply_markup=await volume_buttons())
+
+ elif query.data.lower() == 'seek':
+ if not Config.CALL_STATUS:
+ return await query.answer("Not Playing anything.")
+ #if not (Config.playlist or Config.STREAM_LINK):
+ #return await query.answer("Startup stream cant be seeked.", show_alert=True)
+ await query.answer("trying to seek.")
+ data=Config.DATA.get('FILE_DATA')
+ if not data.get('dur', 0) or \
+ data.get('dur') == 0:
+ return await query.answer("This is a live stream and cannot be seeked.", show_alert=True)
+ k, reply = await seek_file(10)
+ if k == False:
+ return await query.answer(reply, show_alert=True)
await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
- elif query.data.lower() == 'rewind':
- if not Config.CALL_STATUS:
- return await query.answer("Not Playing anything.", show_alert=True)
- if not (Config.playlist or Config.STREAM_LINK):
- return await query.answer("Startup stream cant be seeked.", show_alert=True)
- data=Config.DATA.get('FILE_DATA')
- if not data.get('dur', 0) or \
- data.get('dur') == 0:
- return await query.answer("This stream cant be seeked..", show_alert=True)
- k, reply = await seek_file(-10)
- if k == False:
- return await query.answer(reply, show_alert=True)
- try:
+
+ elif query.data.lower() == 'rewind':
+ if not Config.CALL_STATUS:
+ return await query.answer("Not Playing anything.")
+ #if not (Config.playlist or Config.STREAM_LINK):
+ #return await query.answer("Startup stream cant be seeked.", show_alert=True)
+ await query.answer("trying to rewind.")
+ data=Config.DATA.get('FILE_DATA')
+ if not data.get('dur', 0) or \
+ data.get('dur') == 0:
+ return await query.answer("This is a live stream and cannot be seeked.", show_alert=True)
+ k, reply = await seek_file(-10)
+ if k == False:
+ return await query.answer(reply, show_alert=True)
await query.message.edit_reply_markup(reply_markup=await get_buttons())
- except MessageNotModified:
- pass
- await query.answer()
+
+ elif query.data == 'restart':
+ if not Config.CALL_STATUS:
+ if not Config.playlist:
+ await query.answer("Player is empty, starting STARTUP_STREAM.")
+ else:
+ await query.answer('Resuming the playlist')
+ await query.answer("Restrating the player")
+ await restart()
+ await query.message.edit(text=await get_playlist_str(), reply_markup=await get_buttons(), disable_web_page_preview=True)
+
+ elif query.data.startswith("volume"):
+ me, you = query.data.split("_")
+ if you == "main":
+ await query.message.edit_reply_markup(reply_markup=await volume_buttons())
+ if you == "add":
+ vol=Config.VOLUME+10
+ if not (1 < vol < 200):
+ return await query.answer("Only 1-200 range accepted.")
+ await volume(vol)
+ Config.VOLUME=vol
+ await query.message.edit_reply_markup(reply_markup=await volume_buttons())
+ elif you == "less":
+ vol=Config.VOLUME-10
+ if not (1 < vol < 200):
+ return await query.answer("Only 1-200 range accepted.")
+ await volume(vol)
+ Config.VOLUME=vol
+ await query.message.edit_reply_markup(reply_markup=await volume_buttons())
+ elif you == "back":
+ await query.message.edit_reply_markup(reply_markup=await get_buttons())
+
+
+ elif query.data in ["is_loop", "is_video", "admin_only", "edit_title", "set_shuffle", "reply_msg", "set_new_chat", "record", "record_video", "record_dim"]:
+ if query.data == "is_loop":
+ Config.IS_LOOP = set_config(Config.IS_LOOP)
+ await query.message.edit_reply_markup(reply_markup=await settings_panel())
+
+ elif query.data == "is_video":
+ Config.IS_VIDEO = set_config(Config.IS_VIDEO)
+ await query.message.edit_reply_markup(reply_markup=await settings_panel())
+ await restart_playout()
+
+ elif query.data == "admin_only":
+ Config.ADMIN_ONLY = set_config(Config.ADMIN_ONLY)
+ await query.message.edit_reply_markup(reply_markup=await settings_panel())
+
+ elif query.data == "edit_title":
+ Config.EDIT_TITLE = set_config(Config.EDIT_TITLE)
+ await query.message.edit_reply_markup(reply_markup=await settings_panel())
+
+ elif query.data == "set_shuffle":
+ Config.SHUFFLE = set_config(Config.SHUFFLE)
+ await query.message.edit_reply_markup(reply_markup=await settings_panel())
+
+ elif query.data == "reply_msg":
+ Config.REPLY_PM = set_config(Config.REPLY_PM)
+ await query.message.edit_reply_markup(reply_markup=await settings_panel())
+
+ elif query.data == "record_dim":
+ if not Config.IS_VIDEO_RECORD:
+ return await query.answer("This cant be used for audio recordings")
+ Config.PORTRAIT=set_config(Config.PORTRAIT)
+ await query.message.edit_reply_markup(reply_markup=(await recorder_settings()))
+ elif query.data == 'record_video':
+ Config.IS_VIDEO_RECORD=set_config(Config.IS_VIDEO_RECORD)
+ await query.message.edit_reply_markup(reply_markup=(await recorder_settings()))
+
+ elif query.data == 'record':
+ if Config.IS_RECORDING:
+ k, msg = await stop_recording()
+ if k == False:
+ await query.answer(msg, show_alert=True)
+ else:
+ await query.answer("Recording Stopped")
+ else:
+ k, msg = await start_record_stream()
+ if k == False:
+ await query.answer(msg, show_alert=True)
+ else:
+ await query.answer("Recording started")
+ await query.message.edit_reply_markup(reply_markup=(await recorder_settings()))
+
+ elif query.data == "set_new_chat":
+ if query.from_user is None:
+ return await query.answer("You cant do scheduling here, since you are an anonymous admin. Schedule from private chat.", show_alert=True)
+ if query.from_user.id in Config.SUDO:
+ await query.answer("Setting up new CHAT")
+ chat=query.message.chat.id
+ if Config.IS_RECORDING:
+ await stop_recording()
+ await cancel_all_schedules()
+ await leave_call()
+ Config.CHAT=chat
+ Config.ADMIN_CACHE=False
+ await restart()
+ await query.message.edit("Succesfully Changed Chat")
+ await sync_to_db()
+ else:
+ await query.answer("This can only be used by SUDO users", show_alert=True)
+ if not Config.DATABASE_URI:
+ await query.answer("No DATABASE found, this changes are saved temporarly and will be reverted on restart. Add MongoDb to make this permanant.")
+ elif query.data.startswith("close"):
+ if "sudo" in query.data:
+ if query.from_user.id in Config.SUDO:
+ await query.message.delete()
+ else:
+ await query.answer("This can only be used by SUDO users")
+ else:
+ if query.message.chat.type != "private" and query.message.reply_to_message:
+ if query.message.reply_to_message.from_user is None:
+ pass
+ elif query.from_user.id != query.message.reply_to_message.from_user.id:
+ return await query.answer("Okda")
+ elif query.from_user.id in Config.ADMINS:
+ pass
+ else:
+ return await query.answer("Okda")
+ await query.answer("Menu Closed")
+ await query.message.delete()
+ await query.answer()
diff --git a/plugins/commands.py b/plugins/commands.py
index b9b2e3fa..b5394944 100644
--- a/plugins/commands.py
+++ b/plugins/commands.py
@@ -12,116 +12,328 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
-import asyncio
-from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, InputMediaDocument
-from utils import is_admin
-from pyrogram import Client, filters
-from utils import update, is_admin
-from config import Config
from logger import LOGGER
+from contextlib import suppress
+from config import Config
+import calendar
+import pytz
+from datetime import datetime
+import asyncio
import os
+from pyrogram.errors.exceptions.bad_request_400 import (
+ MessageIdInvalid,
+ MessageNotModified
+)
+from pyrogram.types import (
+ InlineKeyboardMarkup,
+ InlineKeyboardButton
+)
+from utils import (
+ cancel_all_schedules,
+ edit_config,
+ is_admin,
+ leave_call,
+ restart,
+ restart_playout,
+ stop_recording,
+ sync_to_db,
+ update,
+ is_admin,
+ chat_filter,
+ sudo_filter,
+ delete_messages
+)
+from pyrogram import (
+ Client,
+ filters
+)
+IST = pytz.timezone(Config.TIME_ZONE)
+if Config.DATABASE_URI:
+ from database import db
HOME_TEXT = "Hey [{}](tg://user?id={}) 🙋♂️\n\nIam A Bot Built To Play or Stream Videos In Telegram VoiceChats.\nI Can Stream Any YouTube Video Or A Telegram File Or Even A YouTube Live."
admin_filter=filters.create(is_admin)
@Client.on_message(filters.command(['start', f"start@{Config.BOT_USERNAME}"]))
async def start(client, message):
+ if len(message.command) > 1:
+ if message.command[1] == 'help':
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"Play", callback_data='help_play'),
+ InlineKeyboardButton(f"Settings", callback_data=f"help_settings"),
+ InlineKeyboardButton(f"Recording", callback_data='help_record'),
+ ],
+ [
+ InlineKeyboardButton("Scheduling", callback_data="help_schedule"),
+ InlineKeyboardButton("Controling", callback_data='help_control'),
+ InlineKeyboardButton("Admins", callback_data="help_admin"),
+ ],
+ [
+ InlineKeyboardButton(f"Misc", callback_data='help_misc'),
+ InlineKeyboardButton("Close", callback_data="close"),
+ ],
+ ]
+ )
+ await message.reply("Learn to use the VCPlayer, Showing help menu, Choose from the below options.",
+ reply_markup=reply_markup,
+ disable_web_page_preview=True
+ )
+ elif 'sch' in message.command[1]:
+ msg=await message.reply("Checking schedules..")
+ you, me = message.command[1].split("_", 1)
+ who=Config.SCHEDULED_STREAM.get(me)
+ if not who:
+ return await msg.edit("Something gone somewhere.")
+ del Config.SCHEDULED_STREAM[me]
+ whom=f"{message.chat.id}_{msg.message_id}"
+ Config.SCHEDULED_STREAM[whom] = who
+ await sync_to_db()
+ if message.from_user.id not in Config.ADMINS:
+ return await msg.edit("OK da")
+ today = datetime.now(IST)
+ smonth=today.strftime("%B")
+ obj = calendar.Calendar()
+ thisday = today.day
+ year = today.year
+ month = today.month
+ m=obj.monthdayscalendar(year, month)
+ button=[]
+ button.append([InlineKeyboardButton(text=f"{str(smonth)} {str(year)}",callback_data=f"sch_month_choose_none_none")])
+ days=["Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ f=[]
+ for day in days:
+ f.append(InlineKeyboardButton(text=f"{day}",callback_data=f"day_info_none"))
+ button.append(f)
+ for one in m:
+ f=[]
+ for d in one:
+ year_=year
+ if d < int(today.day):
+ year_ += 1
+ if d == 0:
+ k="\u2063"
+ d="none"
+ else:
+ k=d
+ f.append(InlineKeyboardButton(text=f"{k}",callback_data=f"sch_month_{year_}_{month}_{d}"))
+ button.append(f)
+ button.append([InlineKeyboardButton("Close", callback_data="schclose")])
+ await msg.edit(f"Choose the day of the month you want to schedule the voicechat.\nToday is {thisday} {smonth} {year}. Chooosing a date preceeding today will be considered as next year {year+1}", reply_markup=InlineKeyboardMarkup(button))
+
+
+
+ return
buttons = [
[
InlineKeyboardButton('⚙️ Update Channel', url='https://t.me/subin_works'),
InlineKeyboardButton('🧩 Source', url='https://github.com/subinps/VCPlayerBot')
],
[
- InlineKeyboardButton('👨🏼🦯 Help', callback_data='help'),
+ InlineKeyboardButton('👨🏼🦯 Help', callback_data='help_main'),
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
]
]
reply_markup = InlineKeyboardMarkup(buttons)
- await message.reply(HOME_TEXT.format(message.from_user.first_name, message.from_user.id), reply_markup=reply_markup)
+ k = await message.reply(HOME_TEXT.format(message.from_user.first_name, message.from_user.id), reply_markup=reply_markup)
+ await delete_messages([message, k])
@Client.on_message(filters.command(["help", f"help@{Config.BOT_USERNAME}"]))
async def show_help(client, message):
- buttons = [
+ reply_markup=InlineKeyboardMarkup(
[
- InlineKeyboardButton('⚙️ Update Channel', url='https://t.me/subin_works'),
- InlineKeyboardButton('🧩 Source', url='https://github.com/subinps/VCPlayerBot'),
+ [
+ InlineKeyboardButton("Play", callback_data='help_play'),
+ InlineKeyboardButton("Settings", callback_data=f"help_settings"),
+ InlineKeyboardButton("Recording", callback_data='help_record'),
+ ],
+ [
+ InlineKeyboardButton("Scheduling", callback_data="help_schedule"),
+ InlineKeyboardButton("Controling", callback_data='help_control'),
+ InlineKeyboardButton("Admins", callback_data="help_admin"),
+ ],
+ [
+ InlineKeyboardButton("Misc", callback_data='help_misc'),
+ InlineKeyboardButton("Config Vars", callback_data='help_env'),
+ InlineKeyboardButton("Close", callback_data="close"),
+ ],
]
- ]
- reply_markup = InlineKeyboardMarkup(buttons)
+ )
+ if message.chat.type != "private" and message.from_user is None:
+ k=await message.reply(
+ text="I cant help you here, since you are an anonymous admin. Get help in PM",
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"Help", url=f"https://telegram.dog/{Config.BOT_USERNAME}?start=help"),
+ ]
+ ]
+ ),)
+ await delete_messages([message, k])
+ return
if Config.msg.get('help') is not None:
await Config.msg['help'].delete()
Config.msg['help'] = await message.reply_text(
- Config.HELP,
- reply_markup=reply_markup
+ "Learn to use the VCPlayer, Showing help menu, Choose from the below options.",
+ reply_markup=reply_markup,
+ disable_web_page_preview=True
)
+ #await delete_messages([message])
@Client.on_message(filters.command(['repo', f"repo@{Config.BOT_USERNAME}"]))
async def repo_(client, message):
buttons = [
[
InlineKeyboardButton('🧩 Repository', url='https://github.com/subinps/VCPlayerBot'),
- InlineKeyboardButton('⚙️ Update Channel', url='https://t.me/subin_works'),
-
+ InlineKeyboardButton('⚙️ Update Channel', url='https://t.me/subin_works'),
],
+ [
+ InlineKeyboardButton("🎞 How to Deploy", url='https://youtu.be/mnWgZMrNe_0'),
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
+ ]
]
- await message.reply("The source code of this bot is public and can be found at VCPlayerBot.\nYou can deploy your own bot and use in your group.\n\nFeel free to star☀️ the repo if you liked it 🙃.", reply_markup=InlineKeyboardMarkup(buttons))
+ await message.reply("The source code of this bot is public and can be found at VCPlayerBot.\nYou can deploy your own bot and use in your group.\n\nFeel free to star☀️ the repo if you liked it 🙃.", reply_markup=InlineKeyboardMarkup(buttons), disable_web_page_preview=True)
+ await delete_messages([message])
-@Client.on_message(filters.command(['restart', 'update', f"restart@{Config.BOT_USERNAME}", f"update@{Config.BOT_USERNAME}"]) & admin_filter)
+@Client.on_message(filters.command(['restart', 'update', f"restart@{Config.BOT_USERNAME}", f"update@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def update_handler(client, message):
if Config.HEROKU_APP:
- await message.reply("Heroku APP found, Restarting app to update.")
+ k = await message.reply("Heroku APP found, Restarting app to update.")
+ if Config.DATABASE_URI:
+ msg = {"msg_id":k.message_id, "chat_id":k.chat.id}
+ if not await db.is_saved("RESTART"):
+ db.add_config("RESTART", msg)
+ else:
+ await db.edit_config("RESTART", msg)
+ await sync_to_db()
else:
- await message.reply("No Heroku APP found, Trying to restart.")
+ k = await message.reply("No Heroku APP found, Trying to restart.")
+ if Config.DATABASE_URI:
+ msg = {"msg_id":k.message_id, "chat_id":k.chat.id}
+ if not await db.is_saved("RESTART"):
+ db.add_config("RESTART", msg)
+ else:
+ await db.edit_config("RESTART", msg)
+ await message.delete()
await update()
-@Client.on_message(filters.command(['logs', f"logs@{Config.BOT_USERNAME}"]) & admin_filter)
+@Client.on_message(filters.command(['logs', f"logs@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def get_logs(client, message):
- logs=[]
- if os.path.exists("ffmpeg.txt"):
- logs.append(InputMediaDocument("ffmpeg.txt", caption="FFMPEG Logs"))
- if os.path.exists("ffmpeg.txt"):
- logs.append(InputMediaDocument("botlog.txt", caption="Bot Logs"))
- if logs:
- try:
- await message.reply_media_group(logs)
- except:
- await message.reply("Errors occured while uploading log file.")
- pass
- logs.clear()
+ m=await message.reply("Checking logs..")
+ if os.path.exists("botlog.txt"):
+ await message.reply_document('botlog.txt', caption="Bot Logs")
+ await m.delete()
+ await delete_messages([message])
else:
- await message.reply("No log files found.")
+ k = await m.edit("No log files found.")
+ await delete_messages([message, k])
-@Client.on_message(filters.command(['env', f"env@{Config.BOT_USERNAME}"]) & filters.user(Config.SUDO))
+@Client.on_message(filters.command(['env', f"env@{Config.BOT_USERNAME}", "config", f"config@{Config.BOT_USERNAME}"]) & sudo_filter & chat_filter)
async def set_heroku_var(client, message):
- if not Config.HEROKU_APP:
- buttons = [[InlineKeyboardButton('Heroku API_KEY', url='https://dashboard.heroku.com/account/applications/authorizations/new')]]
- await message.reply(
- text="No heroku app found, this command needs the following heroku vars to be set.\n\n1. HEROKU_API_KEY
: Your heroku account api key.\n2. HEROKU_APP_NAME
: Your heroku app name.",
- reply_markup=InlineKeyboardMarkup(buttons))
- return
- if " " in message.text:
- cmd, env = message.text.split(" ", 1)
- if not "=" in env:
- return await message.reply("You should specify the value for env.\nExample: /env CHAT=-100213658211")
- var, value = env.split("=", 2)
- config = Config.HEROKU_APP.config()
- if not value:
- m=await message.reply(f"No value for env specified. Trying to delete env {var}.")
- await asyncio.sleep(2)
- if var in config:
- del config[var]
+ with suppress(MessageIdInvalid, MessageNotModified):
+ m = await message.reply("Checking config vars..")
+ if " " in message.text:
+ cmd, env = message.text.split(" ", 1)
+ if not "=" in env:
+ await m.edit("You should specify the value for env.\nExample: /env CHAT=-100213658211")
+ await delete_messages([message, m])
+ return
+ var, value = env.split("=", 2)
+ else:
+ await m.edit("You haven't provided any value for env, you should follow the correct format.\nExample: /env CHAT=-1020202020202
to change or set CHAT var.\n/env REPLY_MESSAGE= To delete REPLY_MESSAGE.")
+ await delete_messages([message, m])
+ return
+
+ if Config.DATABASE_URI and var in ["STARTUP_STREAM", "CHAT", "LOG_GROUP", "REPLY_MESSAGE", "DELAY", "RECORDING_DUMP"]:
+ await m.edit("Mongo DB Found, Setting up config vars...")
+ await asyncio.sleep(2)
+ if not value:
+ await m.edit(f"No value for env specified. Trying to delete env {var}.")
+ await asyncio.sleep(2)
+ if var in ["STARTUP_STREAM", "CHAT", "DELAY"]:
+ await m.edit("This is a mandatory var and cannot be deleted.")
+ await delete_messages([message, m])
+ return
+ await edit_config(var, False)
await m.edit(f"Sucessfully deleted {var}")
- config[var] = None
+ await delete_messages([message, m])
+ return
else:
- await m.edit(f"No env named {var} found. Nothing was changed.")
- return
- if var in config:
- m=await message.reply(f"Variable already found. Now edited to {value}")
+ if var in ["CHAT", "LOG_GROUP", "RECORDING_DUMP"]:
+ try:
+ value=int(value)
+ except:
+ await m.edit("You should give me a chat id . It should be an interger.")
+ await delete_messages([message, m])
+ return
+ if var == "CHAT":
+ await leave_call()
+ Config.ADMIN_CACHE=False
+ if Config.IS_RECORDING:
+ await stop_recording()
+ await cancel_all_schedules()
+ Config.CHAT=int(value)
+ await restart()
+ await edit_config(var, int(value))
+ await m.edit(f"Succesfully changed {var} to {value}")
+ await delete_messages([message, m])
+ return
+ else:
+ if var == "STARTUP_STREAM":
+ Config.STREAM_SETUP=False
+ await edit_config(var, value)
+ await m.edit(f"Succesfully changed {var} to {value}")
+ await delete_messages([message, m])
+ await restart_playout()
+ return
else:
- m=await message.reply(f"Variable not found, Now setting as new var.")
- await asyncio.sleep(2)
- await m.edit(f"Succesfully set {var} with value {value}, Now Restarting to take effect of changes...")
- config[var] = str(value)
- else:
- await message.reply("You haven't provided any value for env, you should follow the correct format.\nExample: /env CHAT=-1020202020202
to change or set CHAT var.\n/env REPLY_MESSAGE= To delete REPLY_MESSAGE.")
\ No newline at end of file
+ if not Config.HEROKU_APP:
+ buttons = [[InlineKeyboardButton('Heroku API_KEY', url='https://dashboard.heroku.com/account/applications/authorizations/new'), InlineKeyboardButton('🗑 Close', callback_data='close'),]]
+ await m.edit(
+ text="No heroku app found, this command needs the following heroku vars to be set.\n\n1. HEROKU_API_KEY
: Your heroku account api key.\n2. HEROKU_APP_NAME
: Your heroku app name.",
+ reply_markup=InlineKeyboardMarkup(buttons))
+ await delete_messages([message])
+ return
+ config = Config.HEROKU_APP.config()
+ if not value:
+ await m.edit(f"No value for env specified. Trying to delete env {var}.")
+ await asyncio.sleep(2)
+ if var in ["STARTUP_STREAM", "CHAT", "DELAY", "API_ID", "API_HASH", "BOT_TOKEN", "SESSION_STRING", "ADMINS"]:
+ await m.edit("These are mandatory vars and cannot be deleted.")
+ await delete_messages([message, m])
+ return
+ if var in config:
+ await m.edit(f"Sucessfully deleted {var}")
+ await asyncio.sleep(2)
+ await m.edit("Now restarting the app to make changes.")
+ if Config.DATABASE_URI:
+ msg = {"msg_id":m.message_id, "chat_id":m.chat.id}
+ if not await db.is_saved("RESTART"):
+ db.add_config("RESTART", msg)
+ else:
+ await db.edit_config("RESTART", msg)
+ del config[var]
+ config[var] = None
+ else:
+ k = await m.edit(f"No env named {var} found. Nothing was changed.")
+ await delete_messages([message, k])
+ return
+ if var in config:
+ await m.edit(f"Variable already found. Now edited to {value}")
+ else:
+ await m.edit(f"Variable not found, Now setting as new var.")
+ await asyncio.sleep(2)
+ await m.edit(f"Succesfully set {var} with value {value}, Now Restarting to take effect of changes...")
+ if Config.DATABASE_URI:
+ msg = {"msg_id":m.message_id, "chat_id":m.chat.id}
+ if not await db.is_saved("RESTART"):
+ db.add_config("RESTART", msg)
+ else:
+ await db.edit_config("RESTART", msg)
+ config[var] = str(value)
+
+
+
+
diff --git a/plugins/controls.py b/plugins/controls.py
index 5457e4b6..f5b377c1 100644
--- a/plugins/controls.py
+++ b/plugins/controls.py
@@ -12,135 +12,252 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
-from utils import get_playlist_str, is_admin, mute, restart_playout, skip, pause, resume, unmute, volume, get_buttons, is_admin, seek_file, get_player_string
-from pyrogram import Client, filters
+from logger import LOGGER
from pyrogram.types import Message
from config import Config
-from logger import LOGGER
+from pyrogram import (
+ Client,
+ filters
+)
+from utils import (
+ clear_db_playlist,
+ get_playlist_str,
+ is_admin,
+ mute,
+ restart_playout,
+ settings_panel,
+ skip,
+ pause,
+ resume,
+ unmute,
+ volume,
+ get_buttons,
+ is_admin,
+ seek_file,
+ delete_messages,
+ chat_filter,
+ volume_buttons
+)
admin_filter=filters.create(is_admin)
-@Client.on_message(filters.command(["playlist", f"playlist@{Config.BOT_USERNAME}"]) & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["playlist", f"playlist@{Config.BOT_USERNAME}"]) & chat_filter)
async def player(client, message):
+ if not Config.CALL_STATUS:
+ await message.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([message])
+ return
pl = await get_playlist_str()
if message.chat.type == "private":
await message.reply_text(
pl,
disable_web_page_preview=True,
+ reply_markup=await get_buttons(),
)
else:
- if Config.msg.get('playlist') is not None:
- await Config.msg['playlist'].delete()
- Config.msg['playlist'] = await message.reply_text(
+ if Config.msg.get('player') is not None:
+ await Config.msg['player'].delete()
+ Config.msg['player'] = await message.reply_text(
pl,
disable_web_page_preview=True,
+ reply_markup=await get_buttons(),
)
+ await delete_messages([message])
-@Client.on_message(filters.command(["skip", f"skip@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["skip", f"skip@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def skip_track(_, m: Message):
+ msg=await m.reply('trying to skip from queue..')
+ if not Config.CALL_STATUS:
+ await msg.edit(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
if not Config.playlist:
- await m.reply("Playlist is Empty.\nLive Streaming.")
+ await msg.edit("Playlist is Empty.")
+ await delete_messages([m, msg])
return
if len(m.command) == 1:
await skip()
else:
+ #https://github.com/callsmusic/tgvc-userbot/blob/dev/plugins/vc/player.py#L268-L288
try:
items = list(dict.fromkeys(m.command[1:]))
items = [int(x) for x in items if x.isdigit()]
items.sort(reverse=True)
for i in items:
if 2 <= i <= (len(Config.playlist) - 1):
+ await msg.edit(f"Succesfully Removed from Playlist- {i}. **{Config.playlist[i][1]}**")
+ await clear_db_playlist(song=Config.playlist[i])
Config.playlist.pop(i)
- await m.reply(f"Succesfully Removed from Playlist- {i}. **{Config.playlist[i][1]}**")
+ await delete_messages([m, msg])
else:
- await m.reply(f"You Cant Skip First Two Songs- {i}")
+ await msg.edit(f"You cant skip first two songs- {i}")
+ await delete_messages([m, msg])
except (ValueError, TypeError):
- await m.reply_text("Invalid input")
+ await msg.edit("Invalid input")
+ await delete_messages([m, msg])
pl=await get_playlist_str()
if m.chat.type == "private":
- await m.reply_text(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ await msg.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
elif not Config.LOG_GROUP and m.chat.type == "supergroup":
- await m.reply_text(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ if Config.msg.get('player'):
+ await Config.msg['player'].delete()
+ Config.msg['player'] = await msg.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ await delete_messages([m])
-@Client.on_message(filters.command(["pause", f"pause@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["pause", f"pause@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def pause_playing(_, m: Message):
- if Config.PAUSE:
- return await m.reply("Already Paused")
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
- await m.reply("Paused Video Call")
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
+ if Config.PAUSE:
+ k = await m.reply("Already Paused")
+ await delete_messages([m, k])
+ return
+ k = await m.reply("Paused Video Call")
await pause()
+ await delete_messages([m, k])
-@Client.on_message(filters.command(["resume", f"resume@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["resume", f"resume@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def resume_playing(_, m: Message):
- if not Config.PAUSE:
- return await m.reply("Nothing paused to resume")
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
- await m.reply("Resumed Video Call")
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
+ if not Config.PAUSE:
+ k = await m.reply("Nothing paused to resume")
+ await delete_messages([m, k])
+ return
+ k = await m.reply("Resumed Video Call")
await resume()
+ await delete_messages([m, k])
-@Client.on_message(filters.command(['volume', f"volume@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(['volume', f"volume@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def set_vol(_, m: Message):
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
if len(m.command) < 2:
- await m.reply_text('You forgot to pass volume (1-200).')
+ await m.reply_text('Change Volume of Your VCPlayer. ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ', reply_markup=await volume_buttons())
+ await delete_messages([m])
return
- await m.reply_text(f"Volume set to {m.command[1]}")
- await volume(int(m.command[1]))
+ if not 1 < int(m.command[1]) < 200:
+ await m.reply_text(f"Only 1-200 range is accepeted. ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ", reply_markup=await volume_buttons())
+ else:
+ await volume(int(m.command[1]))
+ await m.reply_text(f"Succesfully set volume to {m.command[1]} ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ", reply_markup=await volume_buttons())
+ await delete_messages([m])
+
+
-@Client.on_message(filters.command(['mute', f"mute@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(['mute', f"mute@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def set_mute(_, m: Message):
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
if Config.MUTED:
- return await m.reply_text("Already muted.")
+ k = await m.reply_text("Already muted.")
+ await delete_messages([m, k])
+ return
k=await mute()
if k:
- await m.reply_text(f" 🔇 Succesfully Muted ")
+ k = await m.reply_text(f" 🔇 Succesfully Muted ")
+ await delete_messages([m, k])
else:
- await m.reply_text("Already muted.")
+ k = await m.reply_text("Already muted.")
+ await delete_messages([m, k])
-@Client.on_message(filters.command(['unmute', f"unmute@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(['unmute', f"unmute@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def set_unmute(_, m: Message):
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
if not Config.MUTED:
- return await m.reply("Stream already unmuted.")
+ k = await m.reply("Stream already unmuted.")
+ await delete_messages([m, k])
+ return
k=await unmute()
if k:
- await m.reply_text(f"🔊 Succesfully Unmuted ")
+ k = await m.reply_text(f"🔊 Succesfully Unmuted ")
+ await delete_messages([m, k])
+ return
else:
- await m.reply_text("Not muted, already unmuted.")
+ k=await m.reply_text("Not muted, already unmuted.")
+ await delete_messages([m, k])
-@Client.on_message(filters.command(["replay", f"replay@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["replay", f"replay@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def replay_playout(client, m: Message):
+ msg = await m.reply('Checking player')
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
- await m.reply_text(f"Replaying from begining")
+ await msg.edit(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
+ await msg.edit(f"Replaying from begining")
await restart_playout()
+ await delete_messages([m, msg])
-@Client.on_message(filters.command(["player", f"player@{Config.BOT_USERNAME}"]) & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["player", f"player@{Config.BOT_USERNAME}"]) & chat_filter)
async def show_player(client, m: Message):
+ if not Config.CALL_STATUS:
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
data=Config.DATA.get('FILE_DATA')
if not data.get('dur', 0) or \
data.get('dur') == 0:
- title="Playing Live Stream"
+ title="Playing Live Stream ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
else:
if Config.playlist:
- title=f"{Config.playlist[0][1]}"
+ title=f"{Config.playlist[0][1]} ㅤㅤㅤㅤ\n ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
elif Config.STREAM_LINK:
- title=f"Stream Using [Url]({data['file']}) "
+ title=f"Stream Using [Url]({data['file']}) ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
else:
- title=f"Streaming Startup [stream]({Config.STREAM_URL})"
+ title=f"Streaming Startup [stream]({Config.STREAM_URL}) ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
if m.chat.type == "private":
await m.reply_text(
title,
@@ -155,37 +272,59 @@ async def show_player(client, m: Message):
disable_web_page_preview=True,
reply_markup=await get_buttons()
)
+ await delete_messages([m])
-@Client.on_message(filters.command(["seek", f"seek@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["seek", f"seek@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def seek_playout(client, m: Message):
if not Config.CALL_STATUS:
- return await m.reply("Not Playing anything.")
- if not (Config.playlist or Config.STREAM_LINK):
- return await m.reply("Startup stream cant be seeked.")
+ await m.reply_text(
+ "Player is idle, start the player using below button. ㅤㅤㅤ ㅤㅤ",
+ disable_web_page_preview=True,
+ reply_markup=await get_buttons()
+ )
+ await delete_messages([m])
+ return
data=Config.DATA.get('FILE_DATA')
+ k=await m.reply("Trying to seek..")
if not data.get('dur', 0) or \
data.get('dur') == 0:
- return await m.reply("This stream cant be seeked..")
+ await k.edit("This stream cant be seeked.")
+ await delete_messages([m, k])
+ return
if ' ' in m.text:
i, time = m.text.split(" ")
try:
time=int(time)
except:
- return await m.reply('Invalid time specified')
- k, string=await seek_file(time)
- if k == False:
- return await m.reply(string)
- if not data.get('dur', 0) or \
- data.get('dur') == 0:
- title="Playing Live Stream"
+ await k.edit('Invalid time specified')
+ await delete_messages([m, k])
+ return
+ nyav, string=await seek_file(time)
+ if nyav == False:
+ await k.edit(string)
+ await delete_messages([m, k])
+ return
+ if not data.get('dur', 0)\
+ or data.get('dur') == 0:
+ title="Playing Live Stream ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
else:
if Config.playlist:
- title=f"{Config.playlist[0][1]}"
+ title=f"{Config.playlist[0][1]}\nㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
elif Config.STREAM_LINK:
- title=f"Stream Using [Url]({data['file']})"
+ title=f"Stream Using [Url]({data['file']}) ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
else:
- title=f"Streaming Startup [stream]({Config.STREAM_URL})"
- await m.reply(f"🎸{title}", reply_markup=await get_buttons(), disable_web_page_preview=True)
+ title=f"Streaming Startup [stream]({Config.STREAM_URL}) ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
+ if Config.msg.get('player'):
+ await Config.msg['player'].delete()
+ Config.msg['player'] = await k.edit(f"🎸{title}", reply_markup=await get_buttons(), disable_web_page_preview=True)
+ await delete_messages([m])
else:
- await m.reply('No time specified')
+ await k.edit('No time specified')
+ await delete_messages([m, k])
+
+
+@Client.on_message(filters.command(["settings", f"settings@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
+async def settings(client, m: Message):
+ await m.reply(f"Configure Your VCPlayer Settings Here. ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ", reply_markup=await settings_panel(), disable_web_page_preview=True)
+ await delete_messages([m])
diff --git a/plugins/export_import.py b/plugins/export_import.py
index 959cfb49..3c8cdbc0 100644
--- a/plugins/export_import.py
+++ b/plugins/export_import.py
@@ -13,21 +13,39 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from utils import get_buttons, is_admin, get_playlist_str, shuffle_playlist, import_play_list
-from pyrogram import Client, filters
-from pyrogram.types import Message
-from config import Config
from logger import LOGGER
import json
import os
+from pyrogram.types import Message
+from contextlib import suppress
+from config import Config
+from utils import (
+ get_buttons,
+ is_admin,
+ get_playlist_str,
+ shuffle_playlist,
+ import_play_list,
+ delete_messages,
+ chat_filter
+)
+from pyrogram import (
+ Client,
+ filters
+)
+from pyrogram.errors import (
+ MessageNotModified,
+ MessageIdInvalid
+)
+
admin_filter=filters.create(is_admin)
-@Client.on_message(filters.command(["export", f"export@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["export", f"export@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def export_play_list(client, message: Message):
if not Config.playlist:
- await message.reply_text("Playlist is Empty")
+ k=await message.reply_text("Playlist is Empty")
+ await delete_messages([message, k])
return
file=f"{message.chat.id}_{message.message_id}.json"
with open(file, 'w+') as outfile:
@@ -37,27 +55,35 @@ async def export_play_list(client, message: Message):
os.remove(file)
except:
pass
+ await delete_messages([message])
-@Client.on_message(filters.command(["import", f"import@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["import", f"import@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def import_playlist(client, m: Message):
- if m.reply_to_message is not None and m.reply_to_message.document:
- if m.reply_to_message.document.file_name != "PlayList.json":
- k=await m.reply("Invalid PlayList file given. Use @GetPlayListBot to get a playlist file. Or Export your current Playlist using /export.")
- return
- myplaylist=await m.reply_to_message.download()
- status=await m.reply("Trying to get details from playlist.")
- n=await import_play_list(myplaylist)
- if not n:
- await status.edit("Errors Occured while importing playlist.")
- return
- if Config.SHUFFLE:
- await shuffle_playlist()
- pl=await get_playlist_str()
- if m.chat.type == "private":
- await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
- elif not Config.LOG_GROUP and m.chat.type == "supergroup":
- await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ with suppress(MessageIdInvalid, MessageNotModified):
+ if m.reply_to_message is not None and m.reply_to_message.document:
+ if m.reply_to_message.document.file_name != "PlayList.json":
+ k=await m.reply("Invalid PlayList file given. Export your current Playlist using /export.")
+ await delete_messages([m, k])
+ return
+ myplaylist=await m.reply_to_message.download()
+ status=await m.reply("Trying to get details from playlist.")
+ n=await import_play_list(myplaylist)
+ if not n:
+ await status.edit("Errors Occured while importing playlist.")
+ await delete_messages([m, status])
+ return
+ if Config.SHUFFLE:
+ await shuffle_playlist()
+ pl=await get_playlist_str()
+ if m.chat.type == "private":
+ await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ elif not Config.LOG_GROUP and m.chat.type == "supergroup":
+ if Config.msg.get('playlist'):
+ await Config.msg['playlist'].delete()
+ Config.msg['playlist'] = await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ await delete_messages([m])
+ else:
+ await delete_messages([m, status])
else:
- await status.delete()
- else:
- await m.reply("No playList file given. Use @GetPlayListBot or search for a playlist in @DumpPlaylist to get a playlist file.")
+ k = await m.reply("No playList file given.")
+ await delete_messages([m, k])
diff --git a/plugins/inline.py b/plugins/inline.py
index 79806657..1e0a6a7f 100644
--- a/plugins/inline.py
+++ b/plugins/inline.py
@@ -15,10 +15,19 @@
from pyrogram.handlers import InlineQueryHandler
from youtubesearchpython import VideosSearch
-from pyrogram.types import InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardButton, InlineKeyboardMarkup
-from pyrogram import Client, errors
from config import Config
from logger import LOGGER
+from pyrogram.types import (
+ InlineQueryResultArticle,
+ InputTextMessageContent,
+ InlineKeyboardButton,
+ InlineKeyboardMarkup
+)
+from pyrogram import (
+ Client,
+ errors
+)
+
buttons = [
[
diff --git a/plugins/manage_admins.py b/plugins/manage_admins.py
new file mode 100644
index 00000000..faf0b0f1
--- /dev/null
+++ b/plugins/manage_admins.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+# Copyright (C) @subinps
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from logger import LOGGER
+from config import Config
+from pyrogram import (
+ Client,
+ filters
+)
+from utils import (
+ get_admins,
+ sync_to_db,
+ delete_messages,
+ sudo_filter
+)
+
+
+@Client.on_message(filters.command(['vcpromote', f"vcpromote@{Config.BOT_USERNAME}"]) & sudo_filter)
+async def add_admin(client, message):
+ if message.reply_to_message:
+ if message.reply_to_message.from_user.id is None:
+ k = await message.reply("You are an anonymous admin, you can't do this.")
+ await delete_messages([message, k])
+ return
+ user_id=message.reply_to_message.from_user.id
+ user=message.reply_to_message.from_user
+
+ elif ' ' in message.text:
+ c, user = message.text.split(" ", 1)
+ if user.startswith("@"):
+ user=user.replace("@", "")
+ try:
+ user=await client.get_users(user)
+ except Exception as e:
+ k=await message.reply(f"I was unable to locate that user.\nError: {e}")
+ await delete_messages([message, k])
+ return
+ user_id=user.id
+ else:
+ try:
+ user_id=int(user)
+ user=await client.get_users(user_id)
+ except:
+ k=await message.reply(f"You should give a user id or his username with @.")
+ await delete_messages([message, k])
+ return
+ else:
+ k=await message.reply("No user specified, reply to a user with /vcpromote or pass a users user id or username.")
+ await delete_messages([message, k])
+ return
+ if user_id in Config.ADMINS:
+ k = await message.reply("This user is already an admin.")
+ await delete_messages([message, k])
+ return
+ Config.ADMINS.append(user_id)
+ k=await message.reply(f"Succesfully promoted {user.mention} as VC admin")
+ await sync_to_db()
+ await delete_messages([message, k])
+
+
+@Client.on_message(filters.command(['vcdemote', f"vcdemote@{Config.BOT_USERNAME}"]) & sudo_filter)
+async def remove_admin(client, message):
+ if message.reply_to_message:
+ if message.reply_to_message.from_user.id is None:
+ k = await message.reply("You are an anonymous admin, you can't do this.")
+ await delete_messages([message, k])
+ return
+ user_id=message.reply_to_message.from_user.id
+ user=message.reply_to_message.from_user
+ elif ' ' in message.text:
+ c, user = message.text.split(" ", 1)
+ if user.startswith("@"):
+ user=user.replace("@", "")
+ try:
+ user=await client.get_users(user)
+ except Exception as e:
+ k = await message.reply(f"I was unable to locate that user.\nError: {e}")
+ await delete_messages([message, k])
+ return
+ user_id=user.id
+ else:
+ try:
+ user_id=int(user)
+ user=await client.get_users(user_id)
+ except:
+ k = await message.reply(f"You should give a user id or his username with @.")
+ await delete_messages([message, k])
+ return
+ else:
+ k = await message.reply("No user specified, reply to a user with /vcdemote or pass a users user id or username.")
+ await delete_messages([message, k])
+ return
+ if not user_id in Config.ADMINS:
+ k = await message.reply("This user is not an admin yet.")
+ await delete_messages([message, k])
+ return
+ Config.ADMINS.remove(user_id)
+ k = await message.reply(f"Succesfully Demoted {user.mention}")
+ await sync_to_db()
+ await delete_messages([message, k])
+
+
+@Client.on_message(filters.command(['refresh', f"refresh@{Config.BOT_USERNAME}"]) & filters.user(Config.SUDO))
+async def refresh_admins(client, message):
+ Config.ADMIN_CACHE=False
+ await get_admins(Config.CHAT)
+ k = await message.reply("Admin list has been refreshed")
+ await sync_to_db()
+ await delete_messages([message, k])
\ No newline at end of file
diff --git a/plugins/player.py b/plugins/player.py
index 607ece6c..47317134 100644
--- a/plugins/player.py
+++ b/plugins/player.py
@@ -12,222 +12,411 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
-from utils import download, get_admins, is_admin, get_buttons, get_link, import_play_list, leave_call, play, get_playlist_str, send_playlist, shuffle_playlist, start_stream, stream_from_link
-from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
+from logger import LOGGER
from youtube_search import YoutubeSearch
-from pyrogram import Client, filters
+from contextlib import suppress
from pyrogram.types import Message
from youtube_dl import YoutubeDL
from datetime import datetime
from pyrogram import filters
from config import Config
-from logger import LOGGER
import re
+from utils import (
+ add_to_db_playlist,
+ clear_db_playlist,
+ delete_messages,
+ download,
+ get_admins,
+ get_duration,
+ is_admin,
+ get_buttons,
+ get_link,
+ import_play_list,
+ is_audio,
+ leave_call,
+ play,
+ get_playlist_str,
+ send_playlist,
+ shuffle_playlist,
+ start_stream,
+ stream_from_link,
+ chat_filter
+)
+from pyrogram.types import (
+ InlineKeyboardMarkup,
+ InlineKeyboardButton
+ )
+from pyrogram.errors import (
+ MessageIdInvalid,
+ MessageNotModified
+ )
+from pyrogram import (
+ Client,
+ filters
+ )
admin_filter=filters.create(is_admin)
-@Client.on_message(filters.command(["play", f"play@{Config.BOT_USERNAME}"]) & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["play", "fplay", f"play@{Config.BOT_USERNAME}", f"fplay@{Config.BOT_USERNAME}"]) & chat_filter)
async def add_to_playlist(_, message: Message):
- if Config.ADMIN_ONLY == "Y":
- admins = await get_admins(Config.CHAT)
- if message.from_user.id not in admins:
- await message.reply_sticker("CAADBQADsQIAAtILIVYld1n74e3JuQI")
- return
- type=""
- yturl=""
- ysearch=""
- if message.reply_to_message and message.reply_to_message.video:
- msg = await message.reply_text("⚡️ **Checking Telegram Media...**")
- type='video'
- m_video = message.reply_to_message.video
- elif message.reply_to_message and message.reply_to_message.document:
- msg = await message.reply_text("⚡️ **Checking Telegram Media...**")
- m_video = message.reply_to_message.document
- type='video'
- if not "video" in m_video.mime_type:
- return await msg.edit("The given file is invalid")
- else:
- if message.reply_to_message:
- link=message.reply_to_message.text
- regex = r"^(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?"
- match = re.match(regex,link)
- if match:
- type="youtube"
- yturl=link
- elif " " in message.text:
- text = message.text.split(" ", 1)
- query = text[1]
+ with suppress(MessageIdInvalid, MessageNotModified):
+ if Config.ADMIN_ONLY:
+ admins = await get_admins(Config.CHAT)
+ if not (message.from_user is None and message.sender_chat or message.from_user.id in admins):
+ k=await message.reply_sticker("CAADBQADsQIAAtILIVYld1n74e3JuQI")
+ await delete_messages([message, k])
+ return
+ type=""
+ yturl=""
+ ysearch=""
+ if message.command[0] == "fplay":
+ if not (message.from_user is None and message.sender_chat or message.from_user.id in admins):
+ k=await message.reply("This command is only for admins.")
+ await delete_messages([message, k])
+ return
+ msg = await message.reply_text("⚡️ **Checking recived input..**")
+ if message.reply_to_message and message.reply_to_message.video:
+ await msg.edit("⚡️ **Checking Telegram Media...**")
+ type='video'
+ m_video = message.reply_to_message.video
+ elif message.reply_to_message and message.reply_to_message.document:
+ await msg.edit("⚡️ **Checking Telegram Media...**")
+ m_video = message.reply_to_message.document
+ type='video'
+ if not "video" in m_video.mime_type:
+ return await msg.edit("The given file is invalid")
+ elif message.reply_to_message and message.reply_to_message.audio:
+ #if not Config.IS_VIDEO:
+ #return await message.reply("Play from audio file is available only if Video Mode if turned off.\nUse /settings to configure ypur player.")
+ await msg.edit("⚡️ **Checking Telegram Media...**")
+ type='audio'
+ m_video = message.reply_to_message.audio
+ else:
+ if message.reply_to_message and message.reply_to_message.text:
+ query=message.reply_to_message.text
+ elif " " in message.text:
+ text = message.text.split(" ", 1)
+ query = text[1]
+ else:
+ await msg.edit("You Didn't gave me anything to play.Reply to a video or a youtube link or a direct link.")
+ await delete_messages([message, msg])
+ return
regex = r"^(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?"
match = re.match(regex,query)
if match:
type="youtube"
yturl=query
+ elif query.startswith("http"):
+ """if Config.IS_VIDEO:
+ try:
+ width, height = get_height_and_width(query)
+ except:
+ width, height = None, None
+ LOGGER.error("Unable to get video properties within time.")
+ if not width or \
+ not height:
+ await msg.edit("This is an invalid link, provide me a direct link or a youtube link.")
+ await delete_messages([message, msg])
+ return """
+ #else:
+ try:
+ has_audio_ = is_audio(query)
+ except:
+ has_audio_ = False
+ LOGGER.error("Unable to get Audio properties within time.")
+ if not has_audio_:
+ await msg.edit("This is an invalid link, provide me a direct link or a youtube link.")
+ await delete_messages([message, msg])
+ return
+ try:
+ dur=get_duration(query)
+ except:
+ dur=0
+ if dur == 0:
+ await msg.edit("This is a live stream, Use /stream command.")
+ await delete_messages([message, msg])
+ return
+ type="direct"
+ url=query
else:
type="query"
ysearch=query
+ if not message.from_user is None:
+ user=f"[{message.from_user.first_name}](tg://user?id={message.from_user.id})"
+ user_id = message.from_user.id
else:
- await message.reply_text("You Didn't gave me anything to play.Reply to a video or a youtube link.")
- return
- user=f"[{message.from_user.first_name}](tg://user?id={message.from_user.id})"
- if type=="video":
+ user="Anonymous"
+ user_id = "anonymous_admin"
now = datetime.now()
nyav = now.strftime("%d-%m-%Y-%H:%M:%S")
- data={1:m_video.file_name, 2:m_video.file_id, 3:"telegram", 4:user, 5:f"{nyav}_{m_video.file_size}"}
- Config.playlist.append(data)
- await msg.edit("Media added to playlist")
- if type=="youtube" or type=="query":
- if type=="youtube":
- msg = await message.reply_text("⚡️ **Fetching Video From YouTube...**")
- url=yturl
- elif type=="query":
+ if type in ["video", "audio"]:
+ if type == "audio":
+ title=m_video.title
+ else:
+ title=m_video.file_name
+ data={1:title, 2:m_video.file_id, 3:"telegram", 4:user, 5:f"{nyav}_{m_video.file_size}"}
+ if message.command[0] == "fplay":
+ pla = [data] + Config.playlist
+ Config.playlist = pla
+ else:
+ Config.playlist.append(data)
+ await add_to_db_playlist(data)
+ await msg.edit("Media added to playlist")
+ elif type=="youtube" or type=="query":
+ if type=="youtube":
+ await msg.edit("⚡️ **Fetching Video From YouTube...**")
+ url=yturl
+ elif type=="query":
+ try:
+ await msg.edit("⚡️ **Fetching Video From YouTube...**")
+ ytquery=ysearch
+ results = YoutubeSearch(ytquery, max_results=1).to_dict()
+ url = f"https://youtube.com{results[0]['url_suffix']}"
+ title = results[0]["title"][:40]
+ except Exception as e:
+ await msg.edit(
+ "Song not found.\nTry inline mode.."
+ )
+ LOGGER.error(str(e))
+ await delete_messages([message, msg])
+ return
+ else:
+ return
+ ydl_opts = {
+ "geo-bypass": True,
+ "nocheckcertificate": True
+ }
+ ydl = YoutubeDL(ydl_opts)
try:
- msg = await message.reply_text("⚡️ **Fetching Video From YouTube...**")
- ytquery=ysearch
- results = YoutubeSearch(ytquery, max_results=1).to_dict()
- url = f"https://youtube.com{results[0]['url_suffix']}"
- title = results[0]["title"][:40]
+ info = ydl.extract_info(url, False)
except Exception as e:
+ LOGGER.error(e)
await msg.edit(
- "Song not found.\nTry inline mode.."
- )
+ f"YouTube Download Error ❌\nError:- {e}"
+ )
LOGGER.error(str(e))
+ await delete_messages([message, msg])
return
+ title = info["title"]
+ data={1:title, 2:url, 3:"youtube", 4:user, 5:f"{nyav}_{user_id}"}
+ if message.command[0] == "fplay":
+ pla = [data] + Config.playlist
+ Config.playlist = pla
+ else:
+ Config.playlist.append(data)
+ await add_to_db_playlist(data)
+ await msg.edit(f"[{title}]({url}) added to playist", disable_web_page_preview=True)
+ elif type == "direct":
+ data={1:"Music", 2:url, 3:"url", 4:user, 5:f"{nyav}_{user_id}"}
+ if message.command[0] == "fplay":
+ pla = [data] + Config.playlist
+ Config.playlist = pla
+ else:
+ Config.playlist.append(data)
+ await add_to_db_playlist(data)
+ await msg.edit("Link added to playlist")
+ if not Config.CALL_STATUS \
+ and len(Config.playlist) >= 1:
+ await msg.edit("Downloading and Processing...")
+ await download(Config.playlist[0], msg)
+ await play()
+ elif (len(Config.playlist) == 1 and Config.CALL_STATUS):
+ await msg.edit("Downloading and Processing...")
+ await download(Config.playlist[0], msg)
+ await play()
+ elif message.command[0] == "fplay":
+ await msg.edit("Downloading and Processing...")
+ await download(Config.playlist[0], msg)
+ await play()
else:
- return
- ydl_opts = {
- "geo-bypass": True,
- "nocheckcertificate": True
- }
- ydl = YoutubeDL(ydl_opts)
- try:
- info = ydl.extract_info(url, False)
- except Exception as e:
- LOGGER.error(e)
- await msg.edit(
- f"YouTube Download Error ❌\nError:- {e}"
- )
- LOGGER.error(str(e))
- return
- title = info["title"]
- now = datetime.now()
- nyav = now.strftime("%d-%m-%Y-%H:%M:%S")
- data={1:title, 2:url, 3:"youtube", 4:user, 5:f"{nyav}_{message.from_user.id}"}
- Config.playlist.append(data)
- await msg.edit(f"[{title}]({url}) added to playist", disable_web_page_preview=True)
- if len(Config.playlist) == 1:
- m_status = await msg.edit("Downloading and Processing...")
- await download(Config.playlist[0], m_status)
- await play()
- await m_status.delete()
- else:
- await send_playlist()
- pl=await get_playlist_str()
- if message.chat.type == "private":
- await message.reply(pl, reply_markup=await get_buttons() ,disable_web_page_preview=True)
- elif not Config.LOG_GROUP and message.chat.type == "supergroup":
- await message.reply(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
- for track in Config.playlist[:2]:
- await download(track)
+ await send_playlist()
+ await msg.delete()
+ pl=await get_playlist_str()
+ if message.chat.type == "private":
+ await message.reply(pl, reply_markup=await get_buttons() ,disable_web_page_preview=True)
+ elif not Config.LOG_GROUP and message.chat.type == "supergroup":
+ if Config.msg.get('playlist') is not None:
+ await Config.msg['playlist'].delete()
+ Config.msg['playlist']=await message.reply(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ await delete_messages([message])
+ for track in Config.playlist[:2]:
+ await download(track)
-@Client.on_message(filters.command(["leave", f"leave@{Config.BOT_USERNAME}"]) & admin_filter)
+@Client.on_message(filters.command(["leave", f"leave@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def leave_voice_chat(_, m: Message):
- if not Config.CALL_STATUS:
- return await m.reply("Not joined any voicechat.")
+ if not Config.CALL_STATUS:
+ k=await m.reply("Not joined any voicechat.")
+ await delete_messages([m, k])
+ return
await leave_call()
- await m.reply("Succesfully left videochat.")
+ k=await m.reply("Succesfully left videochat.")
+ await delete_messages([m, k])
-@Client.on_message(filters.command(["shuffle", f"shuffle@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["shuffle", f"shuffle@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def shuffle_play_list(client, m: Message):
if not Config.CALL_STATUS:
- return await m.reply("Not joined any voicechat.")
+ k = await m.reply("Not joined any voicechat.")
+ await delete_messages([m, k])
+ return
else:
if len(Config.playlist) > 2:
- await m.reply_text(f"Playlist Shuffled.")
+ k=await m.reply_text(f"Playlist Shuffled.")
await shuffle_playlist()
-
+ await delete_messages([m, k])
else:
- await m.reply_text(f"You cant shuffle playlist with less than 3 songs.")
+ k=await m.reply_text(f"You cant shuffle playlist with less than 3 songs.")
+ await delete_messages([m, k])
-@Client.on_message(filters.command(["clearplaylist", f"clearplaylist@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["clearplaylist", f"clearplaylist@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def clear_play_list(client, m: Message):
- if not Config.CALL_STATUS:
- return await m.reply("Not joined any voicechat.")
if not Config.playlist:
- return await m.reply("Playlist is empty. May be Live streaming.")
- Config.playlist.clear()
- await m.reply_text(f"Playlist Cleared.")
- await start_stream()
+ k = await m.reply("Playlist is empty.")
+ await delete_messages([m, k])
+ return
+ Config.playlist.clear()
+ k=await m.reply_text(f"Playlist Cleared.")
+ await clear_db_playlist(all=True)
+ if Config.IS_LOOP \
+ and not Config.YPLAY:
+ await start_stream()
+ else:
+ await leave_call()
+ await delete_messages([m, k])
-@Client.on_message(filters.command(["yplay", f"yplay@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["yplay", f"yplay@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def yt_play_list(client, m: Message):
- if m.reply_to_message is not None and m.reply_to_message.document:
- if m.reply_to_message.document.file_name != "YouTube_PlayList.json":
- await m.reply("Invalid PlayList file given. Use @GetPlayListBot or search for a playlist in @DumpPlaylist to get a playlist file.")
- return
- ytplaylist=await m.reply_to_message.download()
- status=await m.reply("Trying to get details from playlist.")
- n=await import_play_list(ytplaylist)
- if not n:
- await status.edit("Errors Occured while importing playlist.")
- return
- if Config.SHUFFLE:
- await shuffle_playlist()
- pl=await get_playlist_str()
- if m.chat.type == "private":
- await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
- elif not Config.LOG_GROUP and m.chat.type == "supergroup":
- await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ with suppress(MessageIdInvalid, MessageNotModified):
+ if m.reply_to_message is not None and m.reply_to_message.document:
+ if m.reply_to_message.document.file_name != "YouTube_PlayList.json":
+ k=await m.reply("Invalid PlayList file given. Use @GetPlayListBot or search for a playlist in @DumpPlaylist to get a playlist file.")
+ await delete_messages([m, k])
+ return
+ ytplaylist=await m.reply_to_message.download()
+ status=await m.reply("Trying to get details from playlist.")
+ n=await import_play_list(ytplaylist)
+ if not n:
+ await status.edit("Errors Occured while importing playlist.")
+ await delete_messages([m, status])
+ return
+ if Config.SHUFFLE:
+ await shuffle_playlist()
+ pl=await get_playlist_str()
+ if m.chat.type == "private":
+ await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ elif not Config.LOG_GROUP and m.chat.type == "supergroup":
+ if Config.msg.get("playlist") is not None:
+ await Config.msg['playlist'].delete()
+ Config.msg['playlist']=await status.edit(pl, disable_web_page_preview=True, reply_markup=await get_buttons())
+ await delete_messages([m])
+ else:
+ await delete_messages([m, status])
else:
- await status.delete()
- else:
- await m.reply("No playList file given. Use @GetPlayListBot or search for a playlist in @DumpPlaylist to get a playlist file.")
+ k=await m.reply("No playList file given. Use @GetPlayListBot or search for a playlist in @DumpPlaylist to get a playlist file.")
+ await delete_messages([m, k])
-@Client.on_message(filters.command(["stream", f"stream@{Config.BOT_USERNAME}"]) & admin_filter & (filters.chat(Config.CHAT) | filters.private))
+@Client.on_message(filters.command(["stream", f"stream@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
async def stream(client, m: Message):
- if m.reply_to_message:
- link=m.reply_to_message.text
- elif " " in m.text:
- text = m.text.split(" ", 1)
- link = text[1]
- else:
- return await m.reply("Provide a link to stream!")
- regex = r"^(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?"
- match = re.match(regex,link)
- if match:
- stream_link=await get_link(link)
- if not stream_link:
- return await m.reply("This is an invalid link.")
- else:
- stream_link=link
- k, msg=await stream_from_link(stream_link)
- if k == False:
- await m.reply(msg)
- return
- await m.reply(f"[Streaming]({stream_link}) Started.", disable_web_page_preview=True)
-
+ with suppress(MessageIdInvalid, MessageNotModified):
+ msg=await m.reply("Checking the recived input.")
+ if m.reply_to_message and m.reply_to_message.text:
+ link=m.reply_to_message.text
+ elif " " in m.text:
+ text = m.text.split(" ", 1)
+ link = text[1]
+ else:
+ k = await msg.edit("Provide a link to stream!")
+ await delete_messages([m, k])
+ return
+ regex = r"^(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?"
+ match = re.match(regex,link)
+ if match:
+ stream_link=await get_link(link)
+ if not stream_link:
+ k = await msg.edit("This is an invalid link.")
+ await delete_messages([m, k])
+ return
+ else:
+ stream_link=link
+ """if Config.IS_VIDEO:
+ try:
+ width, height = get_height_and_width(stream_link)
+ except:
+ width, height = None, None
+ LOGGER.error("Unable to get video properties within time.")
+ if not width or \
+ not height:
+ k = await msg.edit("This is an invalid link, provide me a direct link or a youtube link.")
+ await delete_messages([m, k])
+ return"""
+ #else:
+ try:
+ is_audio_ = is_audio(stream_link)
+ except:
+ is_audio_ = False
+ LOGGER.error("Unable to get Audio properties within time.")
+ if not is_audio_:
+ k = await msg.edit("This is an invalid link, provide me a direct link or a youtube link.")
+ await delete_messages([m, k])
+ return
+ try:
+ dur=get_duration(stream_link)
+ except:
+ dur=0
+ if dur != 0:
+ k = await msg.edit("This is not a live stream, Use /play command.")
+ await delete_messages([m, k])
+ return
+ k, msg_=await stream_from_link(stream_link)
+ if k == False:
+ k = await msg.edit(msg_)
+ await delete_messages([m, k])
+ return
+ if Config.msg.get('player'):
+ await Config.msg['player'].delete()
+ Config.msg['player']=await msg.edit(f"[Streaming]({stream_link}) Started. ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ", disable_web_page_preview=True, reply_markup=await get_buttons())
+ await delete_messages([m])
+
-admincmds=["yplay", "leave", "pause", "resume", "skip", "restart", "volume", "shuffle", "clearplaylist", "export", "import", "update", 'replay', 'logs', 'stream', f'stream@{Config.BOT_USERNAME}', f'logs@{Config.BOT_USERNAME}', f"replay@{Config.BOT_USERNAME}", f"yplay@{Config.BOT_USERNAME}", f"leave@{Config.BOT_USERNAME}", f"pause@{Config.BOT_USERNAME}", f"resume@{Config.BOT_USERNAME}", f"skip@{Config.BOT_USERNAME}", f"restart@{Config.BOT_USERNAME}", f"volume@{Config.BOT_USERNAME}", f"shuffle@{Config.BOT_USERNAME}", f"clearplaylist@{Config.BOT_USERNAME}", f"export@{Config.BOT_USERNAME}", f"import@{Config.BOT_USERNAME}", f"update@{Config.BOT_USERNAME}"]
+admincmds=["yplay", "leave", "pause", "resume", "skip", "restart", "volume", "shuffle", "clearplaylist", "export", "import", "update", 'replay', 'logs', 'stream', 'fplay', 'schedule', 'record', 'slist', 'cancel', 'cancelall', 'vcpromote', 'vcdemote', 'refresh', 'rtitle', 'seek', 'mute', 'unmute',
+f'stream@{Config.BOT_USERNAME}', f'logs@{Config.BOT_USERNAME}', f"replay@{Config.BOT_USERNAME}", f"yplay@{Config.BOT_USERNAME}", f"leave@{Config.BOT_USERNAME}", f"pause@{Config.BOT_USERNAME}", f"resume@{Config.BOT_USERNAME}", f"skip@{Config.BOT_USERNAME}",
+f"restart@{Config.BOT_USERNAME}", f"volume@{Config.BOT_USERNAME}", f"shuffle@{Config.BOT_USERNAME}", f"clearplaylist@{Config.BOT_USERNAME}", f"export@{Config.BOT_USERNAME}", f"import@{Config.BOT_USERNAME}", f"update@{Config.BOT_USERNAME}",
+f'play@{Config.BOT_USERNAME}', f'schedule@{Config.BOT_USERNAME}', f'record@{Config.BOT_USERNAME}', f'slist@{Config.BOT_USERNAME}', f'cancel@{Config.BOT_USERNAME}', f'cancelall@{Config.BOT_USERNAME}', f'vcpromote@{Config.BOT_USERNAME}',
+f'vcdemote@{Config.BOT_USERNAME}', f'refresh@{Config.BOT_USERNAME}', f'rtitle@{Config.BOT_USERNAME}', f'seek@{Config.BOT_USERNAME}', f'mute@{Config.BOT_USERNAME}', f'unmute@{Config.BOT_USERNAME}'
+]
-@Client.on_message(filters.command(admincmds) & ~admin_filter & (filters.chat(Config.CHAT) | filters.private))
-async def notforu(_, m: Message):
- await _.send_cached_media(chat_id=m.chat.id, file_id="CAADBQADEgQAAtMJyFVJOe6-VqYVzAI", caption="You Are Not Authorized", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('⚡️Join Here', url='https://t.me/subin_works')]]))
allcmd = ["play", "player", f"play@{Config.BOT_USERNAME}", f"player@{Config.BOT_USERNAME}"] + admincmds
-@Client.on_message(filters.command(allcmd) & ~filters.chat(Config.CHAT) & filters.group)
+@Client.on_message(filters.command(admincmds) & ~admin_filter & chat_filter)
+async def notforu(_, m: Message):
+ k = await _.send_cached_media(chat_id=m.chat.id, file_id="CAADBQADEgQAAtMJyFVJOe6-VqYVzAI", caption="You Are Not Authorized", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('⚡️Join Here', url='https://t.me/subin_works')]]))
+ await delete_messages([m, k])
+
+@Client.on_message(filters.command(allcmd) & ~chat_filter & filters.group)
async def not_chat(_, m: Message):
- buttons = [
- [
- InlineKeyboardButton('⚡️Make Own Bot', url='https://github.com/subinps/VCPlayerBot'),
- InlineKeyboardButton('🧩 Join Here', url='https://t.me/subin_works'),
- ]
- ]
- await m.reply("You can't use this bot in this group, for that you have to make your own bot from the [SOURCE CODE](https://github.com/subinps/VCPlayerBot) below.", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup(buttons))
+ if m.from_user is not None and m.from_user.id in Config.SUDO:
+ buttons = [
+ [
+ InlineKeyboardButton('⚡️Change CHAT', callback_data='set_new_chat'),
+ ],
+ [
+ InlineKeyboardButton('No', callback_data='closesudo'),
+ ]
+ ]
+ await m.reply("This is not the group which i have been configured to play, Do you want to set this group as default CHAT?", reply_markup=InlineKeyboardMarkup(buttons))
+ await delete_messages([m])
+ else:
+ buttons = [
+ [
+ InlineKeyboardButton('⚡️Make Own Bot', url='https://github.com/subinps/VCPlayerBot'),
+ InlineKeyboardButton('🧩 Join Here', url='https://t.me/subin_works'),
+ ]
+ ]
+ await m.reply("You can't use this bot in this group, for that you have to make your own bot from the [SOURCE CODE](https://github.com/subinps/VCPlayerBot) below.", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup(buttons))
diff --git a/plugins/recorder.py b/plugins/recorder.py
new file mode 100644
index 00000000..3d7440ff
--- /dev/null
+++ b/plugins/recorder.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# Copyright (C) @subinps
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+from logger import LOGGER
+from config import Config
+from pyrogram import (
+ Client,
+ filters
+)
+from utils import (
+ chat_filter,
+ is_admin,
+ is_admin,
+ delete_messages,
+ recorder_settings,
+ sync_to_db
+)
+from pyrogram.types import (
+ InlineKeyboardMarkup,
+ InlineKeyboardButton
+)
+
+admin_filter=filters.create(is_admin)
+
+
+@Client.on_message(filters.command(["record", f"record@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
+async def record_vc(bot, message):
+ await message.reply("Configure you VCPlayer Recording settings from hereㅤㅤ ㅤ", reply_markup=(await recorder_settings()))
+ await delete_messages([message])
+
+@Client.on_message(filters.command(["rtitle", f"rtitle@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
+async def recording_title(bot, message):
+ m=await message.reply("Checking..")
+ if " " in message.text:
+ cmd, title = message.text.split(" ", 1)
+ else:
+ await m.edit("Give me a new title. Use /rtitle < Custom Title >\nUse False
to revert to default title")
+ await delete_messages([message, m])
+ return
+
+ if Config.DATABASE_URI:
+ await m.edit("Mongo DB Found, Setting up recording title...")
+ if title == "False":
+ await m.edit(f"Sucessfully removed custom recording title.")
+ Config.RECORDING_TITLE=False
+ await sync_to_db()
+ await delete_messages([message, m])
+ return
+ else:
+ Config.RECORDING_TITLE=title
+ await sync_to_db()
+ await m.edit(f"Succesfully changed recording title to {title}")
+ await delete_messages([message, m])
+ return
+ else:
+ if not Config.HEROKU_APP:
+ buttons = [[InlineKeyboardButton('Heroku API_KEY', url='https://dashboard.heroku.com/account/applications/authorizations/new'), InlineKeyboardButton('🗑 Close', callback_data='close'),]]
+ await m.edit(
+ text="No heroku app found, this command needs the following heroku vars to be set.\n\n1. HEROKU_API_KEY
: Your heroku account api key.\n2. HEROKU_APP_NAME
: Your heroku app name.",
+ reply_markup=InlineKeyboardMarkup(buttons))
+ await delete_messages([message])
+ return
+ config = Config.HEROKU_APP.config()
+ if title == "False":
+ if "RECORDING_TITLE" in config:
+ await m.edit(f"Sucessfully removed custom recording title. Now restarting..")
+ await delete_messages([message])
+ del config["RECORDING_TITLE"]
+ config["RECORDING_TITLE"] = None
+ else:
+ await m.edit(f"Its already default title, nothing was changed")
+ Config.RECORDING_TITLE=False
+ await delete_messages([message, m])
+ else:
+ await m.edit(f"Succesfully changed recording title to {title}, Now restarting")
+ await delete_messages([message])
+ config["RECORDING_TITLE"] = title
\ No newline at end of file
diff --git a/plugins/scheduler.py b/plugins/scheduler.py
new file mode 100644
index 00000000..96c581a8
--- /dev/null
+++ b/plugins/scheduler.py
@@ -0,0 +1,302 @@
+#!/usr/bin/env python3
+# Copyright (C) @subinps
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from logger import LOGGER
+import re
+import calendar
+from datetime import datetime
+from contextlib import suppress
+import pytz
+from config import Config
+from youtube_search import YoutubeSearch
+from youtube_dl import YoutubeDL
+
+from pyrogram import(
+ Client,
+ filters
+ )
+from pyrogram.types import (
+ InlineKeyboardButton,
+ InlineKeyboardMarkup
+)
+from utils import (
+ delete_messages,
+ is_admin,
+ sync_to_db,
+ is_audio,
+ chat_filter,
+ scheduler
+)
+
+from pyrogram.types import (
+ InlineKeyboardButton,
+ InlineKeyboardMarkup
+)
+from pyrogram.errors import (
+ MessageIdInvalid,
+ MessageNotModified
+)
+
+
+IST = pytz.timezone(Config.TIME_ZONE)
+
+admin_filter=filters.create(is_admin)
+
+
+
+@Client.on_message(filters.command(["schedule", f"schedule@{Config.BOT_USERNAME}"]) & chat_filter & admin_filter)
+async def schedule_vc(bot, message):
+ with suppress(MessageIdInvalid, MessageNotModified):
+ type=""
+ yturl=""
+ ysearch=""
+ msg = await message.reply_text("⚡️ **Checking recived input..**")
+ if message.reply_to_message and message.reply_to_message.video:
+ await msg.edit("⚡️ **Checking Telegram Media...**")
+ type='video'
+ m_video = message.reply_to_message.video
+ elif message.reply_to_message and message.reply_to_message.document:
+ await msg.edit("⚡️ **Checking Telegram Media...**")
+ m_video = message.reply_to_message.document
+ type='video'
+ if not "video" in m_video.mime_type:
+ return await msg.edit("The given file is invalid")
+ elif message.reply_to_message and message.reply_to_message.audio:
+ #if not Config.IS_VIDEO:
+ #return await message.reply("Play from audio file is available only if Video Mode if turned off.\nUse /settings to configure ypur player.")
+ await msg.edit("⚡️ **Checking Telegram Media...**")
+ type='audio'
+ m_video = message.reply_to_message.audio
+ else:
+ if message.reply_to_message and message.reply_to_message.text:
+ query=message.reply_to_message.text
+ elif " " in message.text:
+ text = message.text.split(" ", 1)
+ query = text[1]
+ else:
+ await msg.edit("You Didn't gave me anything to schedule. Reply to a video or a youtube link or a direct link.")
+ await delete_messages([message, msg])
+ return
+ regex = r"^(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?"
+ match = re.match(regex,query)
+ if match:
+ type="youtube"
+ yturl=query
+ elif query.startswith("http"):
+ """if Config.IS_VIDEO:
+ try:
+ width, height = get_height_and_width(query)
+ except:
+ width, height = None, None
+ LOGGER.error("Unable to get video properties within time.")
+ if not width or \
+ not height:
+ await msg.edit("This is an invalid link, provide me a direct link or a youtube link.")
+ await delete_messages([message, msg])
+ return """
+ #else:
+ try:
+ has_audio_ = is_audio(query)
+ except:
+ has_audio_ = False
+ LOGGER.error("Unable to get Audio properties within time.")
+ if not has_audio_:
+ await msg.edit("This is an invalid link, provide me a direct link or a youtube link.")
+ await delete_messages([message, msg])
+ return
+ type="direct"
+ url=query
+ else:
+ type="query"
+ ysearch=query
+ if not message.from_user is None:
+ user=f"[{message.from_user.first_name}](tg://user?id={message.from_user.id}) - (Scheduled)"
+ user_id = message.from_user.id
+ else:
+ user="Anonymous - (Scheduled)"
+ user_id = "anonymous_admin"
+ now = datetime.now()
+ nyav = now.strftime("%d-%m-%Y-%H:%M:%S")
+ if type in ["video", "audio"]:
+ if type == "audio":
+ title=m_video.title
+ else:
+ title=m_video.file_name
+ data={'1':title, '2':m_video.file_id, '3':"telegram", '4':user, '5':f"{nyav}_{m_video.file_size}"}
+ sid=f"{message.chat.id}_{msg.message_id}"
+ Config.SCHEDULED_STREAM[sid] = data
+ await sync_to_db()
+ elif type=="youtube" or type=="query":
+ if type=="youtube":
+ await msg.edit("⚡️ **Fetching Video From YouTube...**")
+ url=yturl
+ elif type=="query":
+ try:
+ await msg.edit("⚡️ **Fetching Video From YouTube...**")
+ ytquery=ysearch
+ results = YoutubeSearch(ytquery, max_results=1).to_dict()
+ url = f"https://youtube.com{results[0]['url_suffix']}"
+ title = results[0]["title"][:40]
+ except Exception as e:
+ await msg.edit(
+ "Song not found.\nTry inline mode.."
+ )
+ LOGGER.error(str(e))
+ await delete_messages([message, msg])
+ return
+ else:
+ return
+ ydl_opts = {
+ "quite": True,
+ "geo-bypass": True,
+ "nocheckcertificate": True
+ }
+ ydl = YoutubeDL(ydl_opts)
+ try:
+ info = ydl.extract_info(url, False)
+ except Exception as e:
+ LOGGER.error(e)
+ await msg.edit(
+ f"YouTube Download Error ❌\nError:- {e}"
+ )
+ LOGGER.error(str(e))
+ await delete_messages([message, msg])
+ return
+ title = info["title"]
+ data={'1':title, '2':url, '3':"youtube", '4':user, '5':f"{nyav}_{user_id}"}
+ sid=f"{message.chat.id}_{msg.message_id}"
+ Config.SCHEDULED_STREAM[sid] = data
+ await sync_to_db()
+ elif type == "direct":
+ data={"1":"Music", '2':url, '3':"url", '4':user, '5':f"{nyav}_{user_id}"}
+ sid=f"{message.chat.id}_{msg.message_id}"
+ Config.SCHEDULED_STREAM[sid] = data
+ await sync_to_db()
+ if message.chat.type!='private' and message.from_user is None:
+ await msg.edit(
+ text="You cant schedule from here since you are an anonymous admin. Click the schedule button to schedule through private chat.",
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"Schedule", url=f"https://telegram.dog/{Config.BOT_USERNAME}?start=sch_{sid}"),
+ ]
+ ]
+ ),)
+ await delete_messages([message, msg])
+ return
+ today = datetime.now(IST)
+ smonth=today.strftime("%B")
+ obj = calendar.Calendar()
+ thisday = today.day
+ year = today.year
+ month = today.month
+ m=obj.monthdayscalendar(year, month)
+ button=[]
+ button.append([InlineKeyboardButton(text=f"{str(smonth)} {str(year)}",callback_data=f"sch_month_choose_none_none")])
+ days=["Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun"]
+ f=[]
+ for day in days:
+ f.append(InlineKeyboardButton(text=f"{day}",callback_data=f"day_info_none"))
+ button.append(f)
+ for one in m:
+ f=[]
+ for d in one:
+ year_=year
+ if d < int(today.day):
+ year_ += 1
+ if d == 0:
+ k="\u2063"
+ d="none"
+ else:
+ k=d
+ f.append(InlineKeyboardButton(text=f"{k}",callback_data=f"sch_month_{year_}_{month}_{d}"))
+ button.append(f)
+ button.append([InlineKeyboardButton("Close", callback_data="schclose")])
+ await msg.edit(f"Choose the day of the month you want to schedule the voicechat.\nToday is {thisday} {smonth} {year}. Chooosing a date preceeding today will be considered as next year {year+1}", reply_markup=InlineKeyboardMarkup(button))
+
+
+
+
+@Client.on_message(filters.command(["slist", f"slist@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
+async def list_schedule(bot, message):
+ k=await message.reply("Checking schedules...")
+ if not Config.SCHEDULE_LIST:
+ await k.edit("Nothing scheduled to play.")
+ await delete_messages([k, message])
+ return
+ text="Current Schedules:\n\n"
+ s=Config.SCHEDULE_LIST
+ f=1
+ for sch in s:
+ details=Config.SCHEDULED_STREAM.get(sch['job_id'])
+ if not details['3']=="telegram":
+ text+=f"{f}. Title: [{details['1']}]({details['2']}) By {details['4']}\n"
+ else:
+ text+=f"{f}. Title: {details['1']} By {details['4']}\n"
+ date = sch['date']
+ f+=1
+ date_=((pytz.utc.localize(date, is_dst=None).astimezone(IST)).replace(tzinfo=None)).strftime("%b %d %Y, %I:%M %p")
+ text+=f"Shedule ID : {sch['job_id']}
\nSchedule Date : {date_}
\n\n"
+
+ await k.edit(text, disable_web_page_preview=True)
+ await delete_messages([message])
+
+
+@Client.on_message(filters.command(["cancel", f"cancel@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
+async def delete_sch(bot, message):
+ with suppress(MessageIdInvalid, MessageNotModified):
+ m = await message.reply("Finding the scheduled stream..")
+ if " " in message.text:
+ cmd, job_id = message.text.split(" ", 1)
+ else:
+ buttons = [
+ [
+ InlineKeyboardButton('Cancel All Schedules', callback_data='schcancel'),
+ InlineKeyboardButton('No', callback_data='schclose'),
+ ]
+ ]
+ reply_markup = InlineKeyboardMarkup(buttons)
+ await m.edit("No Schedule ID specified!! Do you want to Cancel all scheduled streams? or you can find schedul id using /slist command.", reply_markup=reply_markup)
+ await delete_messages([message])
+ return
+ data=Config.SCHEDULED_STREAM.get(job_id)
+ if not data:
+ await m.edit("You gave me an invalid schedule ID, check again and send.")
+ await delete_messages([message, m])
+ return
+ del Config.SCHEDULED_STREAM[job_id]
+ k=scheduler.get_job(job_id, jobstore=None)
+ if k:
+ scheduler.remove_job(job_id, jobstore=None)
+ old=list(filter(lambda k: k['job_id'] == job_id, Config.SCHEDULE_LIST))
+ if old:
+ for old_ in old:
+ Config.SCHEDULE_LIST.remove(old_)
+ await sync_to_db()
+ await m.edit(f"Succesfully deleted {data['1']} from scheduled list.")
+ await delete_messages([message, m])
+
+@Client.on_message(filters.command(["cancelall", f"cancelall@{Config.BOT_USERNAME}"]) & admin_filter & chat_filter)
+async def delete_all_sch(bot, message):
+ buttons = [
+ [
+ InlineKeyboardButton('Cancel All Schedules', callback_data='schcancel'),
+ InlineKeyboardButton('No', callback_data='schclose'),
+ ]
+ ]
+ reply_markup = InlineKeyboardMarkup(buttons)
+ await message.reply("Do you want to cancel all the scheduled streams?ㅤㅤㅤㅤ ㅤ", reply_markup=reply_markup)
+ await delete_messages([message])
+
+
diff --git a/requirements.txt b/requirements.txt
index 48b91d62..4ace93fc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
git+https://github.com/pyrogram/pyrogram@master
-py-tgcalls==0.8.0
+py-tgcalls==0.8.1b25
tgcrypto
ffmpeg-python
wrapt_timeout_decorator
@@ -7,3 +7,8 @@ youtube_dl
youtube_search_python
youtube_search
heroku3
+pillow
+motor
+dnspython
+pytz
+apscheduler
\ No newline at end of file
diff --git a/start.sh b/start.sh
index cb2e71ef..6ea0b83f 100644
--- a/start.sh
+++ b/start.sh
@@ -1,5 +1,12 @@
echo "Cloning Repo...."
-git clone https://github.com/subinps/VCPlayerBot /VCPlayerBot
+if [[ $BRANCH != "None" ]]
+then
+ echo "Cloning beta branch...."
+ git clone https://github.com/subinps/VCPlayerBot -b $BRANCH /VCPlayerBot
+else
+ echo "Cloning main branch...."
+ git clone https://github.com/subinps/VCPlayerBot /VCPlayerBot
+fi
cd /VCPlayerBot
pip3 install -U -r requirements.txt
echo "Starting Bot...."
diff --git a/userplugins/group_call.py b/userplugins/group_call.py
new file mode 100644
index 00000000..4704219a
--- /dev/null
+++ b/userplugins/group_call.py
@@ -0,0 +1,267 @@
+#!/usr/bin/env python3
+# Copyright (C) @subinps
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from logger import LOGGER
+from pyrogram.errors import BotInlineDisabled
+from pyrogram import Client, filters
+from config import Config
+from user import group_call
+import time
+from asyncio import sleep
+from pyrogram.raw.base import Update
+from pyrogram.raw.functions.channels import GetFullChannel
+from pytgcalls import PyTgCalls
+from pytgcalls.types import Update
+from pyrogram.raw.types import (
+ UpdateGroupCall,
+ GroupCallDiscarded,
+ UpdateGroupCallParticipants
+)
+from pytgcalls.types.groups import (
+ JoinedVoiceChat,
+ LeftVoiceChat
+ )
+from pytgcalls.types.stream import (
+ PausedStream,
+ ResumedStream,
+ MutedStream,
+ UnMutedStream,
+ StreamAudioEnded,
+ StreamVideoEnded
+)
+from utils import (
+ start_record_stream,
+ stop_recording,
+ edit_title,
+ stream_from_link,
+ leave_call,
+ start_stream,
+ skip,
+ sync_to_db,
+ scheduler
+)
+
+async def is_reply(_, client, message):
+ if Config.REPLY_PM:
+ return True
+ else:
+ return False
+reply_filter=filters.create(is_reply)
+
+DUMBED=[]
+async def dumb_it(_, client, message):
+ if Config.RECORDING_DUMP and Config.LISTEN:
+ return True
+ else:
+ return False
+rec_filter=filters.create(dumb_it)
+
+@Client.on_message(reply_filter & filters.private & ~filters.bot & filters.incoming & ~filters.service & ~filters.me & ~filters.chat([777000, 454000]))
+async def reply(client, message):
+ try:
+ inline = await client.get_inline_bot_results(Config.BOT_USERNAME, "ETHO_ORUTHAN_PM_VANNU")
+ m=await client.send_inline_bot_result(
+ message.chat.id,
+ query_id=inline.query_id,
+ result_id=inline.results[0].id,
+ hide_via=True
+ )
+ old=Config.msg.get(message.chat.id)
+ if old:
+ await client.delete_messages(message.chat.id, [old["msg"], old["s"]])
+ Config.msg[message.chat.id]={"msg":m.updates[1].message.id, "s":message.message_id}
+ except BotInlineDisabled:
+ LOGGER.error(f"Error: Inline Mode for @{Config.BOT_USERNAME} is not enabled. Enable from @Botfather to enable PM Permit.")
+ await message.reply(f"{Config.REPLY_MESSAGE}\n\nYou can't use this bot in your group, for that you have to make your own bot from the [SOURCE CODE](https://github.com/subinps/VCPlayerBot) below.", disable_web_page_preview=True)
+ except Exception as e:
+ LOGGER.error(e)
+ pass
+
+
+@Client.on_message(filters.private & filters.media & filters.me & rec_filter)
+async def dumb_to_log(client, message):
+ if message.video and message.video.file_name == "record.mp4":
+ await message.copy(int(Config.RECORDING_DUMP))
+ DUMBED.append("video")
+ if message.audio and message.audio.file_name == "record.ogg":
+ await message.copy(int(Config.RECORDING_DUMP))
+ DUMBED.append("audio")
+ if Config.IS_VIDEO_RECORD:
+ if len(DUMBED) == 2:
+ DUMBED.clear()
+ Config.LISTEN=False
+ else:
+ if len(DUMBED) == 1:
+ DUMBED.clear()
+ Config.LISTEN=False
+
+
+@Client.on_message(filters.service & filters.chat(Config.CHAT))
+async def service_msg(client, message):
+ if message.service == 'voice_chat_started':
+ Config.IS_ACTIVE=True
+ k=scheduler.get_job(str(Config.CHAT), jobstore=None) #scheduled records
+ if k:
+ await start_record_stream()
+ LOGGER.info("Resuming recording..")
+ elif Config.WAS_RECORDING:
+ LOGGER.info("Previous recording was ended unexpectedly, Now resuming recordings.")
+ await start_record_stream()#for unscheduled
+ a = await client.send(
+ GetFullChannel(
+ channel=(
+ await client.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ if a.full_chat.call is not None:
+ Config.CURRENT_CALL=a.full_chat.call.id
+ LOGGER.info("Voice chat started.")
+ await sync_to_db()
+ elif message.service == 'voice_chat_scheduled':
+ LOGGER.info("VoiceChat Scheduled")
+ Config.IS_ACTIVE=False
+ Config.HAS_SCHEDULE=True
+ await sync_to_db()
+ elif message.service == 'voice_chat_ended':
+ Config.IS_ACTIVE=False
+ LOGGER.info("Voicechat ended")
+ Config.CURRENT_CALL=None
+ if Config.IS_RECORDING:
+ Config.WAS_RECORDING=True
+ await stop_recording()
+ await sync_to_db()
+ else:
+ pass
+
+@Client.on_raw_update()
+async def handle_raw_updates(client: Client, update: Update, user: dict, chat: dict):
+ if isinstance(update, UpdateGroupCallParticipants):
+ if not Config.CURRENT_CALL:
+ a = await client.send(
+ GetFullChannel(
+ channel=(
+ await client.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ if a.full_chat.call is not None:
+ Config.CURRENT_CALL=a.full_chat.call.id
+ if Config.CURRENT_CALL and update.call.id == Config.CURRENT_CALL:
+ all=update.participants
+ old=list(filter(lambda k: k.peer.user_id == Config.USER_ID, all))
+ if old:
+ for me in old:
+ if me.volume:
+ Config.VOLUME=round(int(me.volume)/100)
+
+
+ if isinstance(update, UpdateGroupCall) and (update.chat_id == int(-1000000000000-Config.CHAT)):
+ if update.call is None:
+ Config.IS_ACTIVE = False
+ Config.CURRENT_CALL=None
+ LOGGER.warning("No Active Group Calls Found.")
+ if Config.IS_RECORDING:
+ Config.WAS_RECORDING=True
+ await stop_recording()
+ LOGGER.warning("Group call was ended and hence stoping recording.")
+ Config.HAS_SCHEDULE = False
+ await sync_to_db()
+ return
+
+ else:
+ call=update.call
+ if isinstance(call, GroupCallDiscarded):
+ Config.CURRENT_CALL=None
+ Config.IS_ACTIVE=False
+ if Config.IS_RECORDING:
+ Config.WAS_RECORDING=True
+ await stop_recording()
+ LOGGER.warning("Group Call Was ended")
+ Config.CALL_STATUS = False
+ await sync_to_db()
+ return
+ Config.IS_ACTIVE=True
+ Config.CURRENT_CALL=call.id
+ if Config.IS_RECORDING and not call.record_video_active:
+ Config.LISTEN=True
+ await stop_recording()
+ LOGGER.warning("Recording was ended by user, hence stopping the schedules.")
+ return
+ if call.schedule_date:
+ Config.HAS_SCHEDULE=True
+ else:
+ Config.HAS_SCHEDULE=False
+ await sync_to_db()
+
+@group_call.on_raw_update()
+async def handler(client: PyTgCalls, update: Update):
+ if isinstance(update, JoinedVoiceChat):
+ Config.CALL_STATUS = True
+ if Config.EDIT_TITLE:
+ await edit_title()
+ elif isinstance(update, LeftVoiceChat):
+ Config.CALL_STATUS = False
+ elif isinstance(update, PausedStream):
+ Config.DUR['PAUSE'] = time.time()
+ Config.PAUSE=True
+ elif isinstance(update, ResumedStream):
+ pause=Config.DUR.get('PAUSE')
+ if pause:
+ diff = time.time() - pause
+ start=Config.DUR.get('TIME')
+ if start:
+ Config.DUR['TIME']=start+diff
+ Config.PAUSE=False
+ elif isinstance(update, MutedStream):
+ Config.MUTED = True
+ elif isinstance(update, UnMutedStream):
+ Config.MUTED = False
+
+
+
+@group_call.on_stream_end()
+async def handler(client: PyTgCalls, update: Update):
+ if isinstance(update, StreamAudioEnded) or isinstance(update, StreamVideoEnded):
+ if not Config.STREAM_END.get("STATUS"):
+ Config.STREAM_END["STATUS"]=str(update)
+ if Config.STREAM_LINK and len(Config.playlist) == 0:
+ if Config.IS_LOOP:
+ await stream_from_link(Config.STREAM_LINK)
+ else:
+ await leave_call()
+ elif not Config.playlist:
+ if Config.IS_LOOP:
+ await start_stream()
+ else:
+ await leave_call()
+ else:
+ await skip()
+ await sleep(15) #wait for max 15 sec
+ try:
+ del Config.STREAM_END["STATUS"]
+ except:
+ pass
+ else:
+ try:
+ del Config.STREAM_END["STATUS"]
+ except:
+ pass
+
+
+
diff --git a/userplugins/pm_reply.py b/userplugins/pm_reply.py
deleted file mode 100644
index 7fd0cac8..00000000
--- a/userplugins/pm_reply.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) @subinps
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-
-from pyrogram.errors import BotInlineDisabled
-from pyrogram import Client, filters
-from logger import LOGGER
-from config import Config
-
-async def is_reply(_, client, message):
- if Config.REPLY_MESSAGE:
- return True
- else:
- return False
-reply_filter=filters.create(is_reply)
-
-@Client.on_message(reply_filter & filters.private & ~filters.bot & filters.incoming & ~filters.service & ~filters.me & ~filters.chat([777000, 454000]))
-async def reply(client, message):
- try:
- inline = await client.get_inline_bot_results(Config.BOT_USERNAME, "ETHO_ORUTHAN_PM_VANNU")
- m=await client.send_inline_bot_result(
- message.chat.id,
- query_id=inline.query_id,
- result_id=inline.results[0].id,
- hide_via=True
- )
- old=Config.msg.get(message.chat.id)
- if old:
- await client.delete_messages(message.chat.id, [old["msg"], old["s"]])
- Config.msg[message.chat.id]={"msg":m.updates[1].message.id, "s":message.message_id}
- except BotInlineDisabled:
- LOGGER.error(f"Error: Inline Mode for @{Config.BOT_USERNAME} is not enabled. Enable from @Botfather to enable PM Permit.")
- await message.reply(f"{Config.REPLY_MESSAGE}\n\nYou can't use this bot in your group, for that you have to make your own bot from the [SOURCE CODE](https://github.com/subinps/VCPlayerBot) below.", disable_web_page_preview=True)
- except Exception as e:
- LOGGER.error(e)
- pass
\ No newline at end of file
diff --git a/utils.py b/utils.py
index 984745dc..82a48592 100644
--- a/utils.py
+++ b/utils.py
@@ -12,37 +12,73 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+
from logger import LOGGER
try:
- from pytgcalls.types.input_stream import InputAudioStream, InputVideoStream, AudioParameters, VideoParameters
- from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
- from pyrogram.raw.functions.phone import EditGroupCallTitle, CreateGroupCall
- from pytgcalls.exceptions import GroupCallNotFound, NoActiveGroupCall
- from pyrogram.errors.exceptions.bad_request_400 import BadRequest
- from pyrogram.raw.functions.channels import GetFullChannel
- from concurrent.futures import CancelledError
from pyrogram.raw.types import InputChannel
from wrapt_timeout_decorator import timeout
- from pytgcalls.types import Update
- from user import group_call, USER
+ from apscheduler.schedulers.asyncio import AsyncIOScheduler
+ from apscheduler.jobstores.mongodb import MongoDBJobStore
+ from apscheduler.jobstores.base import ConflictingIdError
+ from pyrogram.raw.functions.channels import GetFullChannel
from pytgcalls import StreamType
from youtube_dl import YoutubeDL
- from pytgcalls import PyTgCalls
+ from pyrogram import filters
+ from pymongo import MongoClient
from datetime import datetime
from threading import Thread
from config import Config
- from asyncio import sleep
- from signal import SIGINT
+ from asyncio import sleep
from bot import bot
import subprocess
import asyncio
import random
+ import re
import ffmpeg
import json
import time
import sys
import os
import math
+ from pyrogram.errors.exceptions.bad_request_400 import (
+ BadRequest,
+ ScheduleDateInvalid
+ )
+ from pytgcalls.types.input_stream import (
+ AudioVideoPiped,
+ AudioPiped,
+ AudioImagePiped
+ )
+ from pytgcalls.types.input_stream import (
+ AudioParameters,
+ VideoParameters
+ )
+ from pyrogram.types import (
+ InlineKeyboardButton,
+ InlineKeyboardMarkup,
+ Message
+ )
+ from pyrogram.raw.functions.phone import (
+ EditGroupCallTitle,
+ CreateGroupCall,
+ ToggleGroupCallRecord,
+ StartScheduledGroupCall
+ )
+ from pytgcalls.exceptions import (
+ GroupCallNotFound,
+ NoActiveGroupCall,
+ InvalidVideoProportion
+ )
+ from PIL import (
+ Image,
+ ImageFont,
+ ImageDraw
+ )
+
+ from user import (
+ group_call,
+ USER
+ )
except ModuleNotFoundError:
import os
import sys
@@ -50,8 +86,17 @@
file=os.path.abspath("requirements.txt")
subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-r', file, '--upgrade'])
os.execl(sys.executable, sys.executable, *sys.argv)
-ffmpeg_log = open("ffmpeg.txt", "w+")
+if Config.DATABASE_URI:
+ from database import db
+ monclient = MongoClient(Config.DATABASE_URI)
+ jobstores = {
+ 'default': MongoDBJobStore(client=monclient, database=Config.DATABASE_NAME, collection='scheduler')
+ }
+ scheduler = AsyncIOScheduler(jobstores=jobstores)
+else:
+ scheduler = AsyncIOScheduler()
+scheduler.start()
async def play():
@@ -59,30 +104,114 @@ async def play():
if song[3] == "telegram":
file=Config.GET_FILE.get(song[5])
if not file:
+ await download(song)
+ while not file:
await sleep(1)
+ file=Config.GET_FILE.get(song[5])
+ LOGGER.info("Downloading the file from TG")
while not os.path.exists(file):
await sleep(1)
+ elif song[3] == "direct":
+ file=song[2]
else:
file=await get_link(song[2])
if not file:
await skip()
return False
- audio_file, video_file, width, height = await get_raw_files(file)
+ link, seek, pic, width, height = await chek_the_media(file, title=f"{song[1]}")
+ if not link:
+ LOGGER.warning("Unsupported link, Skiping from queue.")
+ return
await sleep(1)
if Config.STREAM_LINK:
Config.STREAM_LINK=False
- await join_call(audio_file, video_file, width, height)
-
+ await join_call(link, seek, pic, width, height)
+async def schedule_a_play(job_id, date):
+ try:
+ scheduler.add_job(run_schedule, "date", [job_id], id=job_id, run_date=date, max_instances=50, misfire_grace_time=None)
+ except ConflictingIdError:
+ LOGGER.warning("This already scheduled")
+ return
+ if not Config.CALL_STATUS or not Config.IS_ACTIVE:
+ if Config.SCHEDULE_LIST[0]['job_id'] == job_id \
+ and (date - datetime.now()).total_seconds() < 86400:
+ song=Config.SCHEDULED_STREAM.get(job_id)
+ if Config.IS_RECORDING:
+ scheduler.add_job(start_record_stream, "date", id=str(Config.CHAT), run_date=date, max_instances=50, misfire_grace_time=None)
+ try:
+ await USER.send(CreateGroupCall(
+ peer=(await USER.resolve_peer(Config.CHAT)),
+ random_id=random.randint(10000, 999999999),
+ schedule_date=int(date.timestamp()),
+ title=song['1']
+ )
+ )
+ Config.HAS_SCHEDULE=True
+ except ScheduleDateInvalid:
+ LOGGER.error("Unable to schedule VideoChat, since date is invalid")
+ except Exception as e:
+ LOGGER.error(f"Error in scheduling voicechat- {e}")
+ await sync_to_db()
+
+async def run_schedule(job_id):
+ data=Config.SCHEDULED_STREAM.get(job_id)
+ if not data:
+ LOGGER.error("The Scheduled stream was not played, since data is missing")
+ old=filter(lambda k: k['job_id'] == job_id, Config.SCHEDULE_LIST)
+ if old:
+ Config.SCHEDULE_LIST.remove(old)
+ await sync_to_db()
+ pass
+ else:
+ if Config.HAS_SCHEDULE:
+ if not await start_scheduled():
+ LOGGER.error("Scheduled stream skiped, Reason - Unable to start a voice chat.")
+ return
+ data_ = [{1:data['1'], 2:data['2'], 3:data['3'], 4:data['4'], 5:data['5']}]
+ Config.playlist = data_ + Config.playlist
+ await play()
+ LOGGER.info("Starting Scheduled Stream")
+ del Config.SCHEDULED_STREAM[job_id]
+ old=list(filter(lambda k: k['job_id'] == job_id, Config.SCHEDULE_LIST))
+ if old:
+ for old_ in old:
+ Config.SCHEDULE_LIST.remove(old_)
+ if not Config.SCHEDULE_LIST:
+ Config.SCHEDULED_STREAM = {} #clear the unscheduled streams
+ await sync_to_db()
+ if len(Config.playlist) <= 1:
+ return
+ await download(Config.playlist[1])
+
+async def cancel_all_schedules():
+ for sch in Config.SCHEDULE_LIST:
+ job=sch['job_id']
+ k=scheduler.get_job(job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ if Config.SCHEDULED_STREAM.get(job):
+ del Config.SCHEDULED_STREAM[job]
+ Config.SCHEDULE_LIST.clear()
+ await sync_to_db()
+ LOGGER.info("All the schedules are removed")
async def skip():
- if Config.STREAM_LINK and len(Config.playlist) == 0:
+ if Config.STREAM_LINK and len(Config.playlist) == 0 and Config.IS_LOOP:
await stream_from_link()
return
- elif not Config.playlist:
+ elif not Config.playlist \
+ and Config.IS_LOOP:
+ LOGGER.info("Loop Play enabled, switching to STARTUP_STREAM, since playlist is empty.")
await start_stream()
return
+ elif not Config.playlist \
+ and not Config.IS_LOOP:
+ LOGGER.info("Loop Play is disabled, leaving call since playlist is empty.")
+ await leave_call()
+ return
old_track = Config.playlist.pop(0)
+ await clear_db_playlist(song=old_track)
if old_track[3] == "telegram":
file=Config.GET_FILE.get(old_track[5])
try:
@@ -90,10 +219,17 @@ async def skip():
except:
pass
del Config.GET_FILE[old_track[5]]
- if not Config.playlist:
+ if not Config.playlist \
+ and Config.IS_LOOP:
+ LOGGER.info("Loop Play enabled, switching to STARTUP_STREAM, since playlist is empty.")
await start_stream()
return
- LOGGER.warning(f"START PLAYING: {Config.playlist[0][1]}")
+ elif not Config.playlist \
+ and not Config.IS_LOOP:
+ LOGGER.info("Loop Play is disabled, leaving call since playlist is empty.")
+ await leave_call()
+ return
+ LOGGER.info(f"START PLAYING: {Config.playlist[0][1]}")
if Config.DUR.get('PAUSE'):
del Config.DUR['PAUSE']
await play()
@@ -102,112 +238,358 @@ async def skip():
await download(Config.playlist[1])
-async def join_call(audio, video, width, height, seek=False):
- while not os.path.exists(audio) or \
- not os.path.exists(video):
- await skip()
+async def check_vc():
+ a = await bot.send(GetFullChannel(channel=(await bot.resolve_peer(Config.CHAT))))
+ if a.full_chat.call is None:
+ try:
+ LOGGER.info("No active calls found, creating new")
+ await USER.send(CreateGroupCall(
+ peer=(await USER.resolve_peer(Config.CHAT)),
+ random_id=random.randint(10000, 999999999)
+ )
+ )
+ if Config.WAS_RECORDING:
+ await start_record_stream()
+ await sleep(2)
+ return True
+ except Exception as e:
+ LOGGER.error(f"Unable to start new GroupCall :- {e}")
+ return False
+ else:
+ if Config.HAS_SCHEDULE:
+ await start_scheduled()
+ return True
+
+
+async def join_call(link, seek, pic, width, height):
+ if not await check_vc():
+ LOGGER.error("No voice call found and was unable to create a new one. Exiting...")
+ return
+ if Config.HAS_SCHEDULE:
+ await start_scheduled()
if Config.CALL_STATUS:
- play=await change_file(audio, video, width, height)
+ if Config.IS_ACTIVE == False:
+ Config.CALL_STATUS = False
+ return await join_call(link, seek, pic, width, height)
+ play=await change_file(link, seek, pic, width, height)
else:
- play=await join_and_play(audio, video, width, height)
+ play=await join_and_play(link, seek, pic, width, height)
if play == False:
await sleep(1)
- await join_call(audio, video, width, height)
+ await join_call(link, seek, pic, width, height)
await sleep(1)
+ if not seek:
+ Config.DUR["TIME"]=time.time()
+ if Config.EDIT_TITLE:
+ await edit_title()
+ old=Config.GET_FILE.get("old")
+ if old:
+ for file in old:
+ os.remove(f"./downloads/{file}")
+ try:
+ del Config.GET_FILE["old"]
+ except:
+ LOGGER.error("Error in Deleting from dict")
+ pass
+ await send_playlist()
+
+async def start_scheduled():
try:
- call=group_call.get_call(Config.CHAT)
- except GroupCallNotFound:
- return await restart()
+ await USER.send(
+ StartScheduledGroupCall(
+ call=(
+ await USER.send(
+ GetFullChannel(
+ channel=(
+ await USER.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ ).full_chat.call
+ )
+ )
+ if Config.WAS_RECORDING:
+ await start_record_stream()
+ return True
except Exception as e:
- LOGGER.warning(e)
- return await restart()
- if str(call.status) != "playing":
- await restart()
- else:
- if not seek:
- Config.DUR["TIME"]=time.time()
- old=Config.GET_FILE.get("old")
- if old:
- for file in old:
- os.remove(f"./downloads/{file}")
- try:
- del Config.GET_FILE["old"]
- except:
- LOGGER.error("Error in deletion")
- pass
- await send_playlist()
-
+ if 'GROUPCALL_ALREADY_STARTED' in str(e):
+ LOGGER.warning("Already Groupcall Exist")
+ return True
+ else:
+ Config.HAS_SCHEDULE=False
+ return await check_vc()
-async def join_and_play(audio, video, width, height):
+async def join_and_play(link, seek, pic, width, height):
try:
- await group_call.join_group_call(
- int(Config.CHAT),
- InputAudioStream(
- audio,
- AudioParameters(
- bitrate=48000,
- ),
- ),
- InputVideoStream(
- video,
- VideoParameters(
- width=width,
- height=height,
- frame_rate=30,
- ),
-
- ),
- stream_type=StreamType().local_stream
- )
+ if seek:
+ start=str(seek['start'])
+ end=str(seek['end'])
+ if not Config.IS_VIDEO:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioPiped(
+ link,
+ audio_parameters=Config.AUDIO_Q,
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}',
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ if pic:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioImagePiped(
+ link,
+ pic,
+ audio_parameters=Config.AUDIO_Q,
+ video_parameters=Config.VIDEO_Q,
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}', ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ if not width \
+ or not height:
+ LOGGER.error("No Valid Video Found and hence removed from playlist.")
+ return await skip()
+ if Config.BITRATE and Config.FPS:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=VideoParameters(
+ width,
+ height,
+ Config.FPS,
+ ),
+ audio_parameters=AudioParameters(
+ Config.BITRATE
+ ),
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}',
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=Config.VIDEO_Q,
+ audio_parameters=Config.AUDIO_Q,
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}',
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ if not Config.IS_VIDEO:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioPiped(
+ link,
+ audio_parameters=Config.AUDIO_Q,
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ if pic:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioImagePiped(
+ link,
+ pic,
+ video_parameters=Config.VIDEO_Q,
+ audio_parameters=Config.AUDIO_Q,
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ if not width \
+ or not height:
+ LOGGER.error("No Valid Video Found and hence removed from playlist.")
+ return await skip()
+ if Config.FPS and Config.BITRATE:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=VideoParameters(
+ width,
+ height,
+ Config.FPS,
+ ),
+ audio_parameters=AudioParameters(
+ Config.BITRATE
+ ),
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
+ else:
+ await group_call.join_group_call(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=Config.VIDEO_Q,
+ audio_parameters=Config.AUDIO_Q
+ ),
+ stream_type=StreamType().pulse_stream,
+ )
Config.CALL_STATUS=True
+ return True
except NoActiveGroupCall:
try:
- LOGGER.warning("No active calls found, creating new")
+ LOGGER.info("No active calls found, creating new")
await USER.send(CreateGroupCall(
peer=(await USER.resolve_peer(Config.CHAT)),
random_id=random.randint(10000, 999999999)
)
)
+ if Config.WAS_RECORDING:
+ await start_record_stream()
await sleep(2)
await restart_playout()
except Exception as e:
LOGGER.error(f"Unable to start new GroupCall :- {e}")
pass
+ except InvalidVideoProportion:
+ if not Config.FPS and not Config.BITRATE:
+ Config.FPS=20
+ Config.BITRATE=48000
+ await join_and_play(link, seek, pic, width, height)
+ Config.FPS=False
+ Config.BITRATE=False
+ return True
+ else:
+ LOGGER.error("Invalid video")
+ await skip()
except Exception as e:
LOGGER.error(f"Errors Occured while joining, retrying Error- {e}")
return False
-async def change_file(audio, video, width, height):
+async def change_file(link, seek, pic, width, height):
try:
- await group_call.change_stream(
- int(Config.CHAT),
- InputAudioStream(
- audio,
- AudioParameters(
- bitrate=48000,
- ),
- ),
- InputVideoStream(
- video,
- VideoParameters(
- width=width,
- height=height,
- frame_rate=30,
- ),
- ),
- )
+ if seek:
+ start=str(seek['start'])
+ end=str(seek['end'])
+ if not Config.IS_VIDEO:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioPiped(
+ link,
+ audio_parameters=Config.AUDIO_Q,
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}',
+ ),
+ )
+ else:
+ if pic:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioImagePiped(
+ link,
+ pic,
+ audio_parameters=Config.AUDIO_Q,
+ video_parameters=Config.VIDEO_Q,
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}', ),
+ )
+ else:
+ if not width \
+ or not height:
+ LOGGER.error("No Valid Video Found and hence removed from playlist.")
+ return await skip()
+ if Config.FPS and Config.BITRATE:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=VideoParameters(
+ width,
+ height,
+ Config.FPS,
+ ),
+ audio_parameters=AudioParameters(
+ Config.BITRATE
+ ),
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}',
+ ),
+ )
+ else:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=Config.VIDEO_Q,
+ audio_parameters=Config.AUDIO_Q,
+ additional_ffmpeg_parameters=f'-ss {start} -atend -t {end}',
+ ),
+ )
+ else:
+ if not Config.IS_VIDEO:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioPiped(
+ link,
+ audio_parameters=Config.AUDIO_Q
+ ),
+ )
+ else:
+ if pic:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioImagePiped(
+ link,
+ pic,
+ audio_parameters=Config.AUDIO_Q,
+ video_parameters=Config.VIDEO_Q,
+ ),
+ )
+ else:
+ if not width \
+ or not height:
+ LOGGER.error("No Valid Video Found and hence removed from playlist.")
+ return await skip()
+ if Config.FPS and Config.BITRATE:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=VideoParameters(
+ width,
+ height,
+ Config.FPS,
+ ),
+ audio_parameters=AudioParameters(
+ Config.BITRATE,
+ ),
+ ),
+ )
+ else:
+ await group_call.change_stream(
+ int(Config.CHAT),
+ AudioVideoPiped(
+ link,
+ video_parameters=Config.VIDEO_Q,
+ audio_parameters=Config.AUDIO_Q,
+ ),
+ )
+ except InvalidVideoProportion:
+ if not Config.FPS and not Config.BITRATE:
+ Config.FPS=20
+ Config.BITRATE=48000
+ await join_and_play(link, seek, pic, width, height)
+ Config.FPS=False
+ Config.BITRATE=False
+ return True
+ else:
+ LOGGER.error("Invalid video, skipped")
+ await skip()
+ return True
except Exception as e:
- LOGGER.error(f"Errors Occured while joining, retrying Error- {e}")
+ LOGGER.error(f"Error in joining call - {e}")
return False
- if Config.EDIT_TITLE:
- await edit_title()
-
async def seek_file(seektime):
- if not (Config.playlist or Config.STREAM_LINK):
- return False, "No Supported stream found for seeeking."
play_start=int(float(Config.DUR.get('TIME')))
if not play_start:
return False, "Player not yet started"
@@ -217,7 +599,7 @@ async def seek_file(seektime):
return False, "No Streams for seeking"
played=int(float(time.time())) - int(float(play_start))
if data.get("dur", 0) == 0:
- return False, "Seems like a live sttream or startup stream is playing."
+ return False, "Seems like live stream is playing, which cannot be seeked."
total=int(float(data.get("dur", 0)))
trimend = total - played - int(seektime)
trimstart = played + int(seektime)
@@ -225,27 +607,49 @@ async def seek_file(seektime):
return False, "Seeked duration exceeds maximum duration of file"
new_play_start=int(play_start) - int(seektime)
Config.DUR['TIME']=new_play_start
- raw_audio, raw_video, width, height = await get_raw_files(data.get("file"), seek={"start":trimstart, "end":trimend})
- await join_call(raw_audio, raw_video, width, height, seek=True)
+ link, seek, pic, width, height = await chek_the_media(data.get("file"), seek={"start":trimstart, "end":trimend})
+ await join_call(link, seek, pic, width, height)
return True, None
async def leave_call():
- await kill_process()
try:
await group_call.leave_group_call(Config.CHAT)
except Exception as e:
LOGGER.error(f"Errors while leaving call {e}")
- Config.playlist.clear()
+ #Config.playlist.clear()
if Config.STREAM_LINK:
Config.STREAM_LINK=False
Config.CALL_STATUS=False
-
+ if Config.SCHEDULE_LIST:
+ sch=Config.SCHEDULE_LIST[0]
+ if (sch['date'] - datetime.now()).total_seconds() < 86400:
+ song=Config.SCHEDULED_STREAM.get(sch['job_id'])
+ if Config.IS_RECORDING:
+ k=scheduler.get_job(str(Config.CHAT), jobstore=None)
+ if k:
+ scheduler.remove_job(str(Config.CHAT), jobstore=None)
+ scheduler.add_job(start_record_stream, "date", id=str(Config.CHAT), run_date=sch['date'], max_instances=50, misfire_grace_time=None)
+ try:
+ await USER.send(CreateGroupCall(
+ peer=(await USER.resolve_peer(Config.CHAT)),
+ random_id=random.randint(10000, 999999999),
+ schedule_date=int((sch['date']).timestamp()),
+ title=song['1']
+ )
+ )
+ Config.HAS_SCHEDULE=True
+ except ScheduleDateInvalid:
+ LOGGER.error("Unable to schedule VideoChat, since date is invalid")
+ except Exception as e:
+ LOGGER.error(f"Error in scheduling voicechat- {e}")
+ await sync_to_db()
+
+
async def restart():
- await kill_process()
try:
await group_call.leave_group_call(Config.CHAT)
await sleep(2)
@@ -254,10 +658,10 @@ async def restart():
if not Config.playlist:
await start_stream()
return
- LOGGER.warning(f"- START PLAYING: {Config.playlist[0][1]}")
+ LOGGER.info(f"- START PLAYING: {Config.playlist[0][1]}")
await sleep(2)
await play()
- LOGGER.warning("Restarting Playout")
+ LOGGER.info("Restarting Playout")
if len(Config.playlist) <= 1:
return
await download(Config.playlist[1])
@@ -267,22 +671,48 @@ async def restart_playout():
if not Config.playlist:
await start_stream()
return
- LOGGER.warning(f"RESTART PLAYING: {Config.playlist[0][1]}")
+ LOGGER.info(f"RESTART PLAYING: {Config.playlist[0][1]}")
data=Config.DATA.get('FILE_DATA')
if data:
- audio_file, video_file, width, height = await get_raw_files(data['file'])
+ link, seek, pic, width, height = await chek_the_media(data['file'], title=f"{Config.playlist[0][1]}")
+ if not link:
+ LOGGER.warning("Unsupported Link")
+ return
await sleep(1)
if Config.STREAM_LINK:
Config.STREAM_LINK=False
- await join_call(audio_file, video_file, width, height)
+ await join_call(link, seek, pic, width, height)
else:
await play()
if len(Config.playlist) <= 1:
return
await download(Config.playlist[1])
+async def set_up_startup():
+ regex = r"^(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)\&?"
+ match = re.match(regex, Config.STREAM_URL)
+ if match:
+ Config.YSTREAM=True
+ LOGGER.info("YouTube Stream is set as STARTUP STREAM")
+ elif Config.STREAM_URL.startswith("https://t.me/DumpPlaylist"):
+ try:
+ msg_id=Config.STREAM_URL.split("/", 4)[4]
+ Config.STREAM_URL=int(msg_id)
+ Config.YPLAY=True
+ LOGGER.info("YouTube Playlist is set as STARTUP STREAM")
+ except:
+ Config.STREAM_URL="http://j78dp346yq5r-hls-live.5centscdn.com/safari/live.stream/playlist.m3u8"
+ LOGGER.error("Unable to fetch youtube playlist, starting Safari TV")
+ pass
+ else:
+ Config.STREAM_URL=Config.STREAM_URL
+ Config.STREAM_SETUP=True
+
+
-async def start_stream():
+async def start_stream():
+ if not Config.STREAM_SETUP:
+ await set_up_startup()
if Config.YPLAY:
await y_play(Config.STREAM_URL)
return
@@ -290,20 +720,24 @@ async def start_stream():
link=await get_link(Config.STREAM_URL)
else:
link=Config.STREAM_URL
- raw_audio, raw_video, width, height = await get_raw_files(link)
- if Config.playlist:
- Config.playlist.clear()
- await join_call(raw_audio, raw_video, width, height)
+ link, seek, pic, width, height = await chek_the_media(link, title="Startup Stream")
+ if not link:
+ LOGGER.warning("Unsupported link")
+ return False
+ #if Config.playlist:
+ #Config.playlist.clear()
+ await join_call(link, seek, pic, width, height)
async def stream_from_link(link):
- raw_audio, raw_video, width, height = await get_raw_files(link)
- if not raw_audio:
+ link, seek, pic, width, height = await chek_the_media(link)
+ if not link:
+ LOGGER.error("Unable to obtain sufficient information from the given url")
return False, "Unable to obtain sufficient information from the given url"
- if Config.playlist:
- Config.playlist.clear()
+ #if Config.playlist:
+ #Config.playlist.clear()
Config.STREAM_LINK=link
- await join_call(raw_audio, raw_video, width, height)
+ await join_call(link, seek, pic, width, height)
return True, None
@@ -351,76 +785,60 @@ async def download(song, msg=None):
except Exception as e:
LOGGER.error(e)
Config.playlist.remove(song)
+ await clear_db_playlist(song=song)
if len(Config.playlist) <= 1:
return
await download(Config.playlist[1])
-async def get_raw_files(link, seek=False):
- await kill_process()
- Config.GET_FILE["old"] = os.listdir("./downloads")
- new = datetime.now().strftime("%d-%m-%Y-%H:%M:%S")
- raw_audio=f"./downloads/{new}_audio.raw"
- raw_video=f"./downloads/{new}_video.raw"
- #if not os.path.exists(raw_audio):
- #os.mkfifo(raw_audio)
- #if not os.path.exists(raw_video):
- #os.mkfifo(raw_video)
- try:
- width, height = get_height_and_width(link)
- except:
+async def chek_the_media(link, seek=False, pic=False, title="Music"):
+ if not Config.IS_VIDEO:
width, height = None, None
- LOGGER.error("Unable to get video properties within time.")
- if not width or \
- not height:
- Config.STREAM_LINK=False
- await skip()
- return None, None, None, None
+ is_audio_=False
+ try:
+ is_audio_ = is_audio(link)
+ except:
+ is_audio_ = False
+ LOGGER.error("Unable to get Audio properties within time.")
+ if not is_audio_:
+ Config.STREAM_LINK=False
+ await skip()
+ return None, None, None, None, None
+ else:
+ try:
+ width, height = get_height_and_width(link)
+ except:
+ width, height = None, None
+ LOGGER.error("Unable to get video properties within time.")
+ if not width or \
+ not height:
+ is_audio_=False
+ try:
+ is_audio_ = is_audio(link)
+ except:
+ is_audio_ = False
+ LOGGER.error("Unable to get Audio properties within time.")
+ if is_audio_:
+ pic_=await bot.get_messages("DumpPlaylist", 30)
+ photo = "./pic/photo"
+ if not os.path.exists(photo):
+ photo = await pic_.download(file_name=photo)
+ try:
+ dur_=get_duration(link)
+ except:
+ dur_='None'
+ pic = get_image(title, photo, dur_)
+ else:
+ Config.STREAM_LINK=False
+ await skip()
+ return None, None, None, None, None
try:
dur=get_duration(link)
except:
dur=0
- Config.DATA['FILE_DATA']={"file":link, "width":width, "height":height, 'dur':dur}
- if seek:
- start=str(seek['start'])
- end=str(seek['end'])
- command = ["ffmpeg", "-y", "-ss", start, "-i", link, "-t", end, "-f", "s16le", "-ac", "1", "-ar", "48000", raw_audio, "-f", "rawvideo", '-r', '30', '-pix_fmt', 'yuv420p', '-vf', f'scale={width}:{height}', raw_video]
- else:
- command = ["ffmpeg", "-y", "-i", link, "-f", "s16le", "-ac", "1", "-ar", "48000", raw_audio, "-f", "rawvideo", '-r', '30', '-pix_fmt', 'yuv420p', '-vf', f'scale={width}:{height}', raw_video]
- process = await asyncio.create_subprocess_exec(
- *command,
- stdout=ffmpeg_log,
- stderr=asyncio.subprocess.STDOUT,
- )
- while not os.path.exists(raw_audio) or \
- not os.path.exists(raw_video):
- await sleep(1)
- Config.FFMPEG_PROCESSES[Config.CHAT]=process
- return raw_audio, raw_video, width, height
-
-
-async def kill_process():
- process = Config.FFMPEG_PROCESSES.get(Config.CHAT)
- if process:
- try:
- process.send_signal(SIGINT)
- try:
- await asyncio.shield(asyncio.wait_for(process.wait(), 5))
- except CancelledError:
- pass
- if process.returncode is None:
- process.kill()
- try:
- await asyncio.shield(
- asyncio.wait_for(process.wait(), 5))
- except CancelledError:
- pass
- except ProcessLookupError:
- pass
- except Exception as e:
- LOGGER.error(e)
- del Config.FFMPEG_PROCESSES[Config.CHAT]
+ Config.DATA['FILE_DATA']={"file":link, 'dur':dur}
+ return link, seek, pic, width, height
async def edit_title():
@@ -444,14 +862,233 @@ async def edit_title():
LOGGER.error(f"Errors Occured while editing title - {e}")
pass
+async def stop_recording():
+ job=str(Config.CHAT)
+ a = await bot.send(GetFullChannel(channel=(await bot.resolve_peer(Config.CHAT))))
+ if a.full_chat.call is None:
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ Config.IS_RECORDING=False
+ await sync_to_db()
+ return False, "No GroupCall Found"
+ try:
+ await USER.send(
+ ToggleGroupCallRecord(
+ call=(
+ await USER.send(
+ GetFullChannel(
+ channel=(
+ await USER.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ ).full_chat.call,
+ start=False,
+ )
+ )
+ Config.IS_RECORDING=False
+ Config.LISTEN=True
+ await sync_to_db()
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ return True, "Succesfully Stoped Recording"
+ except Exception as e:
+ if 'GROUPCALL_NOT_MODIFIED' in str(e):
+ LOGGER.warning("Already No recording Exist")
+ Config.IS_RECORDING=False
+ await sync_to_db()
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ return False, "No recording was started"
+ else:
+ LOGGER.error(str(e))
+ Config.IS_RECORDING=False
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ await sync_to_db()
+ return False, str(e)
+
+
+
+async def start_record_stream():
+ if Config.IS_RECORDING:
+ await stop_recording()
+ if Config.WAS_RECORDING:
+ Config.WAS_RECORDING=False
+ a = await bot.send(GetFullChannel(channel=(await bot.resolve_peer(Config.CHAT))))
+ job=str(Config.CHAT)
+ if a.full_chat.call is None:
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ return False, "No GroupCall Found"
+ try:
+ if not Config.PORTRAIT:
+ pt = False
+ else:
+ pt = True
+ if not Config.RECORDING_TITLE:
+ tt = None
+ else:
+ tt = Config.RECORDING_TITLE
+ if Config.IS_VIDEO_RECORD:
+ await USER.send(
+ ToggleGroupCallRecord(
+ call=(
+ await USER.send(
+ GetFullChannel(
+ channel=(
+ await USER.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ ).full_chat.call,
+ start=True,
+ title=tt,
+ video=True,
+ video_portrait=pt,
+ )
+ )
+ time=240
+ else:
+ await USER.send(
+ ToggleGroupCallRecord(
+ call=(
+ await USER.send(
+ GetFullChannel(
+ channel=(
+ await USER.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ ).full_chat.call,
+ start=True,
+ title=tt,
+ )
+ )
+ time=86400
+ Config.IS_RECORDING=True
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ try:
+ scheduler.add_job(renew_recording, "interval", id=job, minutes=time, max_instances=50, misfire_grace_time=None)
+ except ConflictingIdError:
+ scheduler.remove_job(job, jobstore=None)
+ scheduler.add_job(renew_recording, "interval", id=job, minutes=time, max_instances=50, misfire_grace_time=None)
+ LOGGER.warning("This already scheduled, rescheduling")
+ await sync_to_db()
+ LOGGER.info("Recording Started")
+ return True, "Succesfully Started Recording"
+ except Exception as e:
+ if 'GROUPCALL_NOT_MODIFIED' in str(e):
+ LOGGER.warning("Already Recording.., stoping and restarting")
+ Config.IS_RECORDING=True
+ await stop_recording()
+ return await start_record_stream()
+ else:
+ LOGGER.error(str(e))
+ Config.IS_RECORDING=False
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ await sync_to_db()
+ return False, str(e)
+
+async def renew_recording():
+ try:
+ job=str(Config.CHAT)
+ a = await bot.send(GetFullChannel(channel=(await bot.resolve_peer(Config.CHAT))))
+ if a.full_chat.call is None:
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ LOGGER.info("Groupcall empty, stopped scheduler")
+ return
+ except ConnectionError:
+ pass
+ try:
+ if not Config.PORTRAIT:
+ pt = False
+ else:
+ pt = True
+ if not Config.RECORDING_TITLE:
+ tt = None
+ else:
+ tt = Config.RECORDING_TITLE
+ if Config.IS_VIDEO_RECORD:
+ await USER.send(
+ ToggleGroupCallRecord(
+ call=(
+ await USER.send(
+ GetFullChannel(
+ channel=(
+ await USER.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ ).full_chat.call,
+ start=True,
+ title=tt,
+ video=True,
+ video_portrait=pt,
+ )
+ )
+ else:
+ await USER.send(
+ ToggleGroupCallRecord(
+ call=(
+ await USER.send(
+ GetFullChannel(
+ channel=(
+ await USER.resolve_peer(
+ Config.CHAT
+ )
+ )
+ )
+ )
+ ).full_chat.call,
+ start=True,
+ title=tt,
+ )
+ )
+ Config.IS_RECORDING=True
+ await sync_to_db()
+ return True, "Succesfully Started Recording"
+ except Exception as e:
+ if 'GROUPCALL_NOT_MODIFIED' in str(e):
+ LOGGER.warning("Already Recording.., stoping and restarting")
+ Config.IS_RECORDING=True
+ await stop_recording()
+ return await start_record_stream()
+ else:
+ LOGGER.error(str(e))
+ Config.IS_RECORDING=False
+ k=scheduler.get_job(job_id=job, jobstore=None)
+ if k:
+ scheduler.remove_job(job, jobstore=None)
+ await sync_to_db()
+ return False, str(e)
async def send_playlist():
if Config.LOG_GROUP:
pl = await get_playlist_str()
- if Config.msg.get('playlist') is not None:
- await Config.msg['playlist'].delete()
- Config.msg['playlist'] = await send_text(pl)
+ if Config.msg.get('player') is not None:
+ await Config.msg['player'].delete()
+ Config.msg['player'] = await send_text(pl)
async def send_text(text):
@@ -480,10 +1117,16 @@ async def import_play_list(file):
f=json.loads(file.read(), object_hook=lambda d: {int(k): v for k, v in d.items()})
for playf in f:
Config.playlist.append(playf)
- if len(Config.playlist) == 1:
- LOGGER.warning("Downloading and Processing...")
+ await add_to_db_playlist(playf)
+ if len(Config.playlist) >= 1 \
+ and not Config.CALL_STATUS:
+ LOGGER.info("Extracting link and Processing...")
+ await download(Config.playlist[0])
+ await play()
+ elif (len(Config.playlist) == 1 and Config.CALL_STATUS):
+ LOGGER.info("Extracting link and Processing...")
await download(Config.playlist[0])
- await play()
+ await play()
if not Config.playlist:
file.close()
try:
@@ -513,21 +1156,23 @@ async def y_play(playlist):
n=await import_play_list(playlistfile)
if not n:
LOGGER.error("Errors Occured While Importing Playlist")
- Config.STREAM_URL="https://www.youtube.com/watch?v=zcrUCvBD16k"
Config.YSTREAM=True
Config.YPLAY=False
- LOGGER.warning("Starting Default Live, 24 News")
- await start_stream()
+ if Config.IS_LOOP:
+ Config.STREAM_URL="https://www.youtube.com/watch?v=zcrUCvBD16k"
+ LOGGER.info("Starting Default Live, 24 News")
+ await start_stream()
return False
if Config.SHUFFLE:
await shuffle_playlist()
except Exception as e:
LOGGER.error("Errors Occured While Importing Playlist", e)
- Config.STREAM_URL="https://www.youtube.com/watch?v=zcrUCvBD16k"
Config.YSTREAM=True
Config.YPLAY=False
- LOGGER.warning("Starting Default Live, 24 News")
- await start_stream()
+ if Config.IS_LOOP:
+ Config.STREAM_URL="https://www.youtube.com/watch?v=zcrUCvBD16k"
+ LOGGER.info("Starting Default Live, 24 News")
+ await start_stream()
return False
@@ -555,10 +1200,10 @@ async def resume():
return False
-
async def volume(volume):
try:
await group_call.change_volume_call(Config.CHAT, volume)
+ Config.VOLUME=int(volume)
except BadRequest:
await restart_playout()
except Exception as e:
@@ -590,16 +1235,20 @@ async def unmute():
async def get_admins(chat):
admins=Config.ADMINS
if not Config.ADMIN_CACHE:
- admins = Config.ADMINS + [626664225]
+ if 626664225 not in admins:
+ admins.append(626664225)
try:
grpadmins=await bot.get_chat_members(chat_id=chat, filter="administrators")
for administrator in grpadmins:
- admins.append(administrator.user.id)
+ if not administrator.user.id in admins:
+ admins.append(administrator.user.id)
except Exception as e:
LOGGER.error(f"Errors occured while getting admin list - {e}")
pass
Config.ADMINS=admins
Config.ADMIN_CACHE=True
+ if Config.DATABASE_URI:
+ await db.edit_config("ADMINS", Config.ADMINS)
return admins
@@ -607,43 +1256,79 @@ async def is_admin(_, client, message: Message):
admins = await get_admins(Config.CHAT)
if message.from_user is None and message.sender_chat:
return True
- if message.from_user.id in admins:
+ elif message.from_user.id in admins:
+ return True
+ else:
+ return False
+
+async def valid_chat(_, client, message: Message):
+ if message.chat.type == "private":
+ return True
+ elif message.chat.id == Config.CHAT:
+ return True
+ elif Config.LOG_GROUP and message.chat.id == Config.LOG_GROUP:
return True
else:
return False
+
+chat_filter=filters.create(valid_chat)
+async def sudo_users(_, client, message: Message):
+ if message.from_user is None and message.sender_chat:
+ return False
+ elif message.from_user.id in Config.SUDO:
+ return True
+ else:
+ return False
+
+sudo_filter=filters.create(sudo_users)
async def get_playlist_str():
- if not Config.playlist:
- pl = f"🔈 Playlist is empty. Streaming [STARTUP_STREAM]({Config.STREAM_URL})"
+ if not Config.CALL_STATUS:
+ pl="Player is idle and no song is playing.ㅤㅤㅤㅤ"
+ if Config.STREAM_LINK:
+ pl = f"🔈 Streaming [Live Stream]({Config.STREAM_LINK}) ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
+ elif not Config.playlist:
+ pl = f"🔈 Playlist is empty. Streaming [STARTUP_STREAM]({Config.STREAM_URL})ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ"
else:
if len(Config.playlist)>=25:
tplaylist=Config.playlist[:25]
pl=f"Listing first 25 songs of total {len(Config.playlist)} songs.\n"
- pl += f"▶️ **Playlist**:\n" + "\n".join([
+ pl += f"▶️ **Playlist**: ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ\n" + "\n".join([
f"**{i}**. **🎸{x[1]}**\n 👤**Requested by:** {x[4]}"
for i, x in enumerate(tplaylist)
])
tplaylist.clear()
else:
- pl = f"▶️ **Playlist**:\n" + "\n".join([
+ pl = f"▶️ **Playlist**: ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ\n" + "\n".join([
f"**{i}**. **🎸{x[1]}**\n 👤**Requested by:** {x[4]}\n"
for i, x in enumerate(Config.playlist)
])
return pl
+
async def get_buttons():
data=Config.DATA.get("FILE_DATA")
- if data.get('dur', 0) == 0:
+ if not Config.CALL_STATUS:
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"🎸 Start the Player", callback_data="restart"),
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
+ ],
+ ]
+ )
+ elif data.get('dur', 0) == 0:
reply_markup=InlineKeyboardMarkup(
[
[
- InlineKeyboardButton(f"{get_player_string()}", callback_data="player"),
+ InlineKeyboardButton(f"{get_player_string()}", callback_data="info_player"),
],
[
InlineKeyboardButton(f"⏯ {get_pause(Config.PAUSE)}", callback_data=f"{get_pause(Config.PAUSE)}"),
- InlineKeyboardButton(f"{'🔇 Unmute' if Config.MUTED else '🔊 Mute'}", callback_data='mute'),
+ InlineKeyboardButton('🔊 Volume Control', callback_data='volume_main'),
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
],
]
)
@@ -651,7 +1336,7 @@ async def get_buttons():
reply_markup=InlineKeyboardMarkup(
[
[
- InlineKeyboardButton(f"{get_player_string()}", callback_data='player'),
+ InlineKeyboardButton(f"{get_player_string()}", callback_data='info_player'),
],
[
InlineKeyboardButton("⏮ Rewind", callback_data='rewind'),
@@ -659,16 +1344,231 @@ async def get_buttons():
InlineKeyboardButton(f"⏭ Seek", callback_data='seek'),
],
[
- InlineKeyboardButton(f"{'🔇 Unmute' if Config.MUTED else '🔊 Mute'}", callback_data='mute'),
InlineKeyboardButton("🔄 Shuffle", callback_data="shuffle"),
InlineKeyboardButton("⏩ Skip", callback_data="skip"),
InlineKeyboardButton("⏮ Replay", callback_data="replay"),
],
+ [
+ InlineKeyboardButton('🔊 Volume Control', callback_data='volume_main'),
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
+ ]
]
)
return reply_markup
+async def settings_panel():
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"Player Mode", callback_data='info_mode'),
+ InlineKeyboardButton(f"{'🔂 Non Stop Playback' if Config.IS_LOOP else '▶️ Play and Leave'}", callback_data='is_loop'),
+ ],
+ [
+ InlineKeyboardButton("🎞 Video", callback_data=f"info_video"),
+ InlineKeyboardButton(f"{'📺 Enabled' if Config.IS_VIDEO else '🎙 Disabled'}", callback_data='is_video'),
+ ],
+ [
+ InlineKeyboardButton("🤴 Admin Only", callback_data=f"info_admin"),
+ InlineKeyboardButton(f"{'🔒 Enabled' if Config.ADMIN_ONLY else '🔓 Disabled'}", callback_data='admin_only'),
+ ],
+ [
+ InlineKeyboardButton("🪶 Edit Title", callback_data=f"info_title"),
+ InlineKeyboardButton(f"{'✏️ Enabled' if Config.EDIT_TITLE else '🚫 Disabled'}", callback_data='edit_title'),
+ ],
+ [
+ InlineKeyboardButton("🔀 Shuffle Mode", callback_data=f"info_shuffle"),
+ InlineKeyboardButton(f"{'✅ Enabled' if Config.SHUFFLE else '🚫 Disabled'}", callback_data='set_shuffle'),
+ ],
+ [
+ InlineKeyboardButton("👮 Auto Reply (PM Permit)", callback_data=f"info_reply"),
+ InlineKeyboardButton(f"{'✅ Enabled' if Config.REPLY_PM else '🚫 Disabled'}", callback_data='reply_msg'),
+ ],
+ [
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
+ ]
+
+ ]
+ )
+ await sync_to_db()
+ return reply_markup
+
+
+async def recorder_settings():
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"{'⏹ Stop Recording' if Config.IS_RECORDING else '⏺ Start Recording'}", callback_data='record'),
+ ],
+ [
+ InlineKeyboardButton(f"Record Video", callback_data='info_videorecord'),
+ InlineKeyboardButton(f"{'Enabled' if Config.IS_VIDEO_RECORD else 'Disabled'}", callback_data='record_video'),
+ ],
+ [
+ InlineKeyboardButton(f"Video Dimension", callback_data='info_videodimension'),
+ InlineKeyboardButton(f"{'Portrait' if Config.PORTRAIT else 'Landscape'}", callback_data='record_dim'),
+ ],
+ [
+ InlineKeyboardButton(f"Custom Recording Title", callback_data='info_rectitle'),
+ InlineKeyboardButton(f"{Config.RECORDING_TITLE if Config.RECORDING_TITLE else 'Default'}", callback_data='info_rectitle'),
+ ],
+ [
+ InlineKeyboardButton(f"Recording Dumb Channel", callback_data='info_recdumb'),
+ InlineKeyboardButton(f"{Config.RECORDING_DUMP if Config.RECORDING_DUMP else 'Not Dumping'}", callback_data='info_recdumb'),
+ ],
+ [
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
+ ]
+ ]
+ )
+ await sync_to_db()
+ return reply_markup
+
+async def volume_buttons():
+ reply_markup=InlineKeyboardMarkup(
+ [
+ [
+ InlineKeyboardButton(f"{get_volume_string()}", callback_data='info_volume'),
+ ],
+ [
+ InlineKeyboardButton(f"{'🔊' if Config.MUTED else '🔇'}", callback_data='mute'),
+ InlineKeyboardButton(f"- 10", callback_data='volume_less'),
+ InlineKeyboardButton(f"+ 10", callback_data='volume_add'),
+ ],
+ [
+ InlineKeyboardButton(f"🔙 Back", callback_data='volume_back'),
+ InlineKeyboardButton('🗑 Close', callback_data='close'),
+ ]
+ ]
+ )
+ return reply_markup
+
+
+async def delete_messages(messages):
+ await asyncio.sleep(Config.DELAY)
+ for msg in messages:
+ try:
+ if msg.chat.type == "supergroup":
+ await msg.delete()
+ except:
+ pass
+
+#Database Config
+async def sync_to_db():
+ if Config.DATABASE_URI:
+ await check_db()
+ await db.edit_config("ADMINS", Config.ADMINS)
+ await db.edit_config("IS_VIDEO", Config.IS_VIDEO)
+ await db.edit_config("IS_LOOP", Config.IS_LOOP)
+ await db.edit_config("REPLY_PM", Config.REPLY_PM)
+ await db.edit_config("ADMIN_ONLY", Config.ADMIN_ONLY)
+ await db.edit_config("SHUFFLE", Config.SHUFFLE)
+ await db.edit_config("EDIT_TITLE", Config.EDIT_TITLE)
+ await db.edit_config("CHAT", Config.CHAT)
+ await db.edit_config("SUDO", Config.SUDO)
+ await db.edit_config("REPLY_MESSAGE", Config.REPLY_MESSAGE)
+ await db.edit_config("LOG_GROUP", Config.LOG_GROUP)
+ await db.edit_config("STREAM_URL", Config.STREAM_URL)
+ await db.edit_config("DELAY", Config.DELAY)
+ await db.edit_config("SCHEDULED_STREAM", Config.SCHEDULED_STREAM)
+ await db.edit_config("SCHEDULE_LIST", Config.SCHEDULE_LIST)
+ await db.edit_config("IS_VIDEO_RECORD", Config.IS_VIDEO_RECORD)
+ await db.edit_config("IS_RECORDING", Config.IS_RECORDING)
+ await db.edit_config("WAS_RECORDING", Config.WAS_RECORDING)
+ await db.edit_config("PORTRAIT", Config.PORTRAIT)
+ await db.edit_config("RECORDING_DUMP", Config.RECORDING_DUMP)
+ await db.edit_config("RECORDING_TITLE", Config.RECORDING_TITLE)
+ await db.edit_config("HAS_SCHEDULE", Config.HAS_SCHEDULE)
+
+
+
+
+async def sync_from_db():
+ if Config.DATABASE_URI:
+ await check_db()
+ Config.ADMINS = await db.get_config("ADMINS")
+ Config.IS_VIDEO = await db.get_config("IS_VIDEO")
+ Config.IS_LOOP = await db.get_config("IS_LOOP")
+ Config.REPLY_PM = await db.get_config("REPLY_PM")
+ Config.ADMIN_ONLY = await db.get_config("ADMIN_ONLY")
+ Config.SHUFFLE = await db.get_config("SHUFFLE")
+ Config.EDIT_TITLE = await db.get_config("EDIT_TITLE")
+ Config.CHAT = int(await db.get_config("CHAT"))
+ Config.playlist = await db.get_playlist()
+ Config.LOG_GROUP = await db.get_config("LOG_GROUP")
+ Config.SUDO = await db.get_config("SUDO")
+ Config.REPLY_MESSAGE = await db.get_config("REPLY_MESSAGE")
+ Config.DELAY = await db.get_config("DELAY")
+ Config.STREAM_URL = await db.get_config("STREAM_URL")
+ Config.SCHEDULED_STREAM = await db.get_config("SCHEDULED_STREAM")
+ Config.SCHEDULE_LIST = await db.get_config("SCHEDULE_LIST")
+ Config.IS_VIDEO_RECORD = await db.get_config('IS_VIDEO_RECORD')
+ Config.IS_RECORDING = await db.get_config("IS_RECORDING")
+ Config.WAS_RECORDING = await db.get_config('WAS_RECORDING')
+ Config.PORTRAIT = await db.get_config("PORTRAIT")
+ Config.RECORDING_DUMP = await db.get_config("RECORDING_DUMP")
+ Config.RECORDING_TITLE = await db.get_config("RECORDING_TITLE")
+ Config.HAS_SCHEDULE = await db.get_config("HAS_SCHEDULE")
+
+async def add_to_db_playlist(song):
+ if Config.DATABASE_URI:
+ song_={str(k):v for k,v in song.items()}
+ db.add_to_playlist(song[5], song_)
+
+async def clear_db_playlist(song=None, all=False):
+ if Config.DATABASE_URI:
+ if all:
+ await db.clear_playlist()
+ else:
+ await db.del_song(song[5])
+
+async def check_db():
+ if not await db.is_saved("ADMINS"):
+ db.add_config("ADMINS", Config.ADMINS)
+ if not await db.is_saved("IS_VIDEO"):
+ db.add_config("IS_VIDEO", Config.IS_VIDEO)
+ if not await db.is_saved("IS_LOOP"):
+ db.add_config("IS_LOOP", Config.IS_LOOP)
+ if not await db.is_saved("REPLY_PM"):
+ db.add_config("REPLY_PM", Config.REPLY_PM)
+ if not await db.is_saved("ADMIN_ONLY"):
+ db.add_config("ADMIN_ONLY", Config.ADMIN_ONLY)
+ if not await db.is_saved("SHUFFLE"):
+ db.add_config("SHUFFLE", Config.SHUFFLE)
+ if not await db.is_saved("EDIT_TITLE"):
+ db.add_config("EDIT_TITLE", Config.EDIT_TITLE)
+ if not await db.is_saved("CHAT"):
+ db.add_config("CHAT", Config.CHAT)
+ if not await db.is_saved("SUDO"):
+ db.add_config("SUDO", Config.SUDO)
+ if not await db.is_saved("REPLY_MESSAGE"):
+ db.add_config("REPLY_MESSAGE", Config.REPLY_MESSAGE)
+ if not await db.is_saved("STREAM_URL"):
+ db.add_config("STREAM_URL", Config.STREAM_URL)
+ if not await db.is_saved("DELAY"):
+ db.add_config("DELAY", Config.DELAY)
+ if not await db.is_saved("LOG_GROUP"):
+ db.add_config("LOG_GROUP", Config.LOG_GROUP)
+ if not await db.is_saved("SCHEDULED_STREAM"):
+ db.add_config("SCHEDULED_STREAM", Config.SCHEDULED_STREAM)
+ if not await db.is_saved("SCHEDULE_LIST"):
+ db.add_config("SCHEDULE_LIST", Config.SCHEDULE_LIST)
+ if not await db.is_saved("IS_VIDEO_RECORD"):
+ db.add_config("IS_VIDEO_RECORD", Config.IS_VIDEO_RECORD)
+ if not await db.is_saved("PORTRAIT"):
+ db.add_config("PORTRAIT", Config.PORTRAIT)
+ if not await db.is_saved("IS_RECORDING"):
+ db.add_config("IS_RECORDING", Config.IS_RECORDING)
+ if not await db.is_saved('WAS_RECORDING'):
+ db.add_config('WAS_RECORDING', Config.WAS_RECORDING)
+ if not await db.is_saved("RECORDING_DUMP"):
+ db.add_config("RECORDING_DUMP", Config.RECORDING_DUMP)
+ if not await db.is_saved("RECORDING_TITLE"):
+ db.add_config("RECORDING_TITLE", Config.RECORDING_TITLE)
+ if not await db.is_saved('HAS_SCHEDULE'):
+ db.add_config("HAS_SCHEDULE", Config.HAS_SCHEDULE)
+
+
async def progress_bar(current, zero, total, start, msg):
now = time.time()
if total == 0:
@@ -688,9 +1588,25 @@ async def progress_bar(current, zero, total, start, msg):
await msg.edit(text=current_message)
except:
pass
- LOGGER.warning(current_message)
+ LOGGER.info(f"Downloading {round(percentage, 2)}% ")
+
+@timeout(10)
+def is_audio(file):
+ try:
+ k=ffmpeg.probe(file)['streams']
+ if k:
+ return True
+ else:
+ return False
+ except KeyError:
+ return False
+ except Exception as e:
+ LOGGER.error(f"Stream Unsupported {e} ")
+ return False
+
+
@timeout(10)#wait for maximum 10 sec, temp fix for ffprobe
def get_height_and_width(file):
try:
@@ -747,9 +1663,28 @@ def get_player_string():
''.join(["━" for i in range(math.floor(percentage / 5))]),
''.join(["─" for i in range(20 - math.floor(percentage / 5))])
)
- finaal=f"{convert(played)} {progressbar} {convert(dur)}"
- return finaal
-
+ final=f"{convert(played)} {progressbar} {convert(dur)}"
+ return final
+
+def get_volume_string():
+ current = int(Config.VOLUME)
+ if current == 0:
+ current += 1
+ if Config.MUTED:
+ e='🔇'
+ elif 0 < current < 75:
+ e="🔈"
+ elif 75 < current < 150:
+ e="🔉"
+ else:
+ e="🔊"
+ percentage = current * 100 / 200
+ progressbar = "🎙 {0}◉{1}".format(\
+ ''.join(["━" for i in range(math.floor(percentage / 5))]),
+ ''.join(["─" for i in range(20 - math.floor(percentage / 5))])
+ )
+ final=f" {str(current)} / {str(200)} {progressbar} {e}"
+ return final
def TimeFormatter(milliseconds: int) -> str:
seconds, milliseconds = divmod(int(milliseconds), 1000)
@@ -763,6 +1698,11 @@ def TimeFormatter(milliseconds: int) -> str:
((str(milliseconds) + " millisec, ") if milliseconds else "")
return tmp[:-2]
+def set_config(value):
+ if value:
+ return False
+ else:
+ return True
def convert(seconds):
seconds = seconds % (24 * 3600)
@@ -785,62 +1725,86 @@ def stop_and_restart():
os.execl(sys.executable, sys.executable, *sys.argv)
+def get_image(title, pic, dur="Live"):
+ newimage = "converted.jpg"
+ image = Image.open(pic)
+ draw = ImageDraw.Draw(image)
+ font = ImageFont.truetype('font.ttf', 70)
+ title = title[0:30]
+ MAX_W = 1790
+ dur=convert(int(float(dur)))
+ if dur=="0:00:00":
+ dur = "Live Stream"
+ para=[f'Playing : {title}', f'Duration: {dur}']
+ current_h, pad = 450, 20
+ for line in para:
+ w, h = draw.textsize(line, font=font)
+ draw.text(((MAX_W - w) / 2, current_h), line, font=font, fill ="skyblue")
+ current_h += h + pad
+ image.save(newimage)
+ return newimage
+
+async def edit_config(var, value):
+ if var == "STARTUP_STREAM":
+ Config.STREAM_URL = value
+ elif var == "CHAT":
+ Config.CHAT = int(value)
+ elif var == "LOG_GROUP":
+ Config.LOG_GROUP = int(value)
+ elif var == "DELAY":
+ Config.DELAY = int(value)
+ elif var == "REPLY_MESSAGE":
+ Config.REPLY_MESSAGE = value
+ elif var == "RECORDING_DUMP":
+ Config.RECORDING_DUMP = value
+ await sync_to_db()
+
+
async def update():
await leave_call()
if Config.HEROKU_APP:
Config.HEROKU_APP.restart()
else:
- await kill_process()
Thread(
target=stop_and_restart()
).start()
+async def startup_check():
+ if Config.LOG_GROUP:
+ try:
+ k=await bot.get_chat_member(Config.LOG_GROUP, Config.BOT_USERNAME)
+ except ValueError:
+ LOGGER.error(f"LOG_GROUP var Found and @{Config.BOT_USERNAME} is not a member of the group.")
+ return False
+ if Config.RECORDING_DUMP:
+ try:
+ k=await USER.get_chat_member(Config.RECORDING_DUMP, Config.USER_ID)
+ except ValueError:
+ LOGGER.error(f"RECORDING_DUMP var Found and @{Config.USER_ID} is not a member of the group./ Channel")
+ return False
+ if not k.status in ["administrator", "creator"]:
+ LOGGER.error(f"RECORDING_DUMP var Found and @{Config.USER_ID} is not a admin of the group./ Channel")
+ return False
+ if Config.CHAT:
+ try:
+ k=await USER.get_chat_member(Config.CHAT, Config.USER_ID)
+ if not k.status in ["administrator", "creator"]:
+ LOGGER.warning(f"{Config.USER_ID} is not an admin in {Config.CHAT}, it is recommended to run the user as admin.")
+ elif k.status in ["administrator", "creator"] and not k.can_manage_voice_chats:
+ LOGGER.warning(f"{Config.USER_ID} is not having right to manage voicechat, it is recommended to promote with this right.")
+ except ValueError:
+ LOGGER.error(f"The user account by which you generated the SESSION_STRING is not found on CHAT ({Config.CHAT})")
+ return False
+ try:
+ k=await bot.get_chat_member(Config.CHAT, Config.BOT_USERNAME)
+ if not k.status == "administrator":
+ LOGGER.warning(f"{Config.BOT_USERNAME}, is not an admin in {Config.CHAT}, it is recommended to run the bot as admin.")
+ except ValueError:
+ LOGGER.warning(f"Bot Was Not Found on CHAT, it is recommended to add {Config.BOT_USERNAME} to {Config.CHAT}")
+ pass
+ if not Config.DATABASE_URI:
+ LOGGER.warning("No DATABASE_URI , found. It is recommended to use a database.")
+ return True
+
-
-@group_call.on_raw_update()
-async def handler(client: PyTgCalls, update: Update):
- if str(update) == "JOINED_VOICE_CHAT":
- Config.CALL_STATUS = True
- if Config.EDIT_TITLE:
- await edit_title()
- elif str(update) == "LEFT_VOICE_CHAT":
- Config.CALL_STATUS = False
- elif str(update) == "PAUSED_STREAM":
- Config.DUR['PAUSE'] = time.time()
- Config.PAUSE=True
- elif str(update) == "RESUMED_STREAM":
- pause=Config.DUR.get('PAUSE')
- if pause:
- diff = time.time() - pause
- start=Config.DUR.get('TIME')
- if start:
- Config.DUR['TIME']=start+diff
- Config.PAUSE=False
- elif str(update) == 'MUTED_STREAM':
- Config.MUTED = True
- elif str(update) == 'UNMUTED_STREAM':
- Config.MUTED = False
-
-
-@group_call.on_stream_end()
-async def handler(client: PyTgCalls, update: Update):
- if str(update) == "STREAM_AUDIO_ENDED" or str(update) == "STREAM_VIDEO_ENDED":
- if not Config.STREAM_END.get("STATUS"):
- Config.STREAM_END["STATUS"]=str(update)
- if Config.STREAM_LINK and len(Config.playlist) == 0:
- await stream_from_link(Config.STREAM_LINK)
- elif not Config.playlist:
- await start_stream()
- else:
- await skip()
- await sleep(15) #wait for max 15 sec
- try:
- del Config.STREAM_END["STATUS"]
- except:
- pass
- else:
- try:
- del Config.STREAM_END["STATUS"]
- except:
- pass