Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kshubham506 committed May 30, 2021
0 parents commit 5886bbc
Show file tree
Hide file tree
Showing 66 changed files with 1,989 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .env copy
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BOT_TOKEN=
API_ID=
MONGO_URL=
API_HASH=
webhookUrl=
WEBHOOK_AUTH=
BOT_URL=https://t.me/vcplayerbot
PARENT_URL=https://t.me/sktechhub
SUPPORT_GROUP=https://t.me/voicechatsupport
HELPER_ACT=
HELPER_ACT_ID=
BOT_ID=
LOG_INSIGHT_KEY=
LOG_INSIGHT_REGION=
BOT_SESSION=
USERBOT_SESSION=
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
venv
songs
images
Logs
.env
.env.local
Binary file added __pycache__/callmanager.cpython-38.pyc
Binary file not shown.
Binary file added __pycache__/dbhandler.cpython-38.pyc
Binary file not shown.
335 changes: 335 additions & 0 deletions callmanager.py

Large diffs are not rendered by default.

77 changes: 77 additions & 0 deletions dbhandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from callmanager import user_app
from utils.Logger import *
import asyncio
from utils.MongoClient import MongoDBClient
from utils.Config import Config
from bson import json_util
import asyncio
import schedule
from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant

mongo_client = MongoDBClient()
config = Config()


async def handle_db_calls():
while True:
try:
# for runtime data
run_data = mongo_client.fetchRunTimeData()
# logInfo(f"Making call to fetch runtime data")
if run_data is not None:
if run_data.get("alowed_chat_types") not in [None]:
config.setAllowedChatTypes(
run_data.get("alowed_chat_types"))
if run_data.get("global_admins") not in [None]:
config.saveGlobalAdmins(run_data.get("global_admins"))
if isinstance(run_data.get("simultaneous_calls"), int) is True:
config.save_simultaneous_calls(
run_data.get("simultaneous_calls"))
if isinstance(run_data.get("playlist_size"), int) is True:
config.save_playlist_size(
run_data.get("playlist_size"))
if run_data.get("playback_footer") not in [None, '']:
config.save_playback_footer(
run_data.get("playback_footer"))
else:
config.save_playback_footer('')
else:
logException("DB handler runtime data is none", True)

# fetch active clients
active_clients = mongo_client.get_all_chats()
# logInfo(
# f"Making call to fetch active clients : {len(active_clients)}")
config.setActiveClients(active_clients)
schedule.run_pending()
except Exception as ex:
logException(f"Error in handle_db_calls: {ex}", True)
finally:
if config.get("env") == "local":
await asyncio.sleep(5)
else:
await asyncio.sleep(30)


async def leaveStaleChats():
try:
left = []
failed = []
distinct_docs = mongo_client.chats_to_disconnect()
for chat in distinct_docs:
try:
chat_id = None
c = chat.get("chat")
if c is not None:
chat_id = c.get("chat_id")
await user_app.leave_chat(chat_id)
left.append(chat_id)
await asyncio.sleep(5)
except UserNotParticipant as e:
left.append(chat_id)
except Exception as ex:
failed.append(chat_id)
logWarning(f"Error in leave chat inside loop {ex}")
logException(f"Left the chats : Done {left} , Failed : {failed} ")
except Exception as ex:
logException(f"Error in leaveStaeChats , {ex}", True)
Binary file added etc/ITC Avant Garde Gothic LT Bold.otf
Binary file not shown.
Binary file added etc/ITC Avant Garde Gothic LT Book Regular.otf
Binary file not shown.
Binary file added etc/KronaOne-Regular.ttf
Binary file not shown.
Binary file added etc/foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added etc/foreground_converted.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions etc/foreground_converted.png:Zone.Identifier
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=about:blank
HostUrl=https://www35.online-convert.com/dl/web7/download-file/c32fcf71-c971-4985-ac1a-9ded3e572501/skumar1.png
Binary file added etc/foreground_converted_rgba.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions helpers/GenerateCover.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import asyncio
import os
import aiofiles
import aiohttp
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import uuid
from utils.Logger import *
from urllib.request import urlopen


# ITC_BOLD = ImageFont.truetype(
# "etc/ITC Avant Garde Gothic LT Bold.otf", 32)
KRONA = ImageFont.truetype("etc/KronaOne-Regular.ttf", 48)
KRONA_52 = ImageFont.truetype("etc/KronaOne-Regular.ttf", 52)
ITC_REG = ImageFont.truetype(
"etc/ITC Avant Garde Gothic LT Book Regular.otf", 48)
KRONA_SMALL = ImageFont.truetype("etc/KronaOne-Regular.ttf", 32)


def changeImageSize(maxWidth, maxHeight, image):
widthRatio = maxWidth / image.size[0]
heightRatio = maxHeight / image.size[1]
newWidth = int(widthRatio * image.size[0])
newHeight = int(heightRatio * image.size[1])
newImage = image.resize((newWidth, newHeight), Image.NEAREST)
return newImage


async def generate_cover(title, thumbnail, result_file_name):
temp_file = uuid.uuid4()
final_img = None
try:
logInfo(
f"Request to generate cover for title : {title} , thumbnail : {thumbnail} , filename : {result_file_name}")
if len(title) == 0:
return None
title = title.strip()
if len(title) > 25:
title = title[:22]+str('...')

downloaded_thumbnail = Image.open(urlopen(thumbnail))

foreground = Image.open(f"etc/foreground_converted_rgba.png")
resized_thumbnail = changeImageSize(1280, 720, downloaded_thumbnail)

background = resized_thumbnail.convert("RGBA")

Image.alpha_composite(background, foreground).save(
f"images/{temp_file}.png", optimize=True, quality=10)

img = Image.open(f"images/{temp_file}.png")
draw = ImageDraw.Draw(img)

draw.text((10, 580), f"Now Playing", fill="white", font=ITC_REG)

draw.text((10, 640), f"{title}", fill="white", font=KRONA_52)

draw.text((985, 20), f"A SkTechHub", fill="white", font=KRONA_SMALL)
draw.text((1100, 50), f"Product", fill="white", font=KRONA_SMALL)

img.save(result_file_name, optimize=True, quality=20)
final_img = result_file_name
except Exception as ex:
logException(f"Error while generating cover : {ex}", True)
final_img = None
finally:
return result_file_name if final_img is not None else None

Empty file added helpers/__init__.py
Empty file.
Binary file added helpers/__pycache__/GenerateCover.cpython-38.pyc
Binary file not shown.
Binary file added helpers/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added helpers/__pycache__/decorators.cpython-38.pyc
Binary file not shown.
Binary file added helpers/__pycache__/fromatMessages.cpython-38.pyc
Binary file not shown.
Binary file added helpers/__pycache__/queues.cpython-38.pyc
Binary file not shown.
121 changes: 121 additions & 0 deletions helpers/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import asyncio
from typing import Callable
from bson import json_util
from pyrogram import Client
from pyrogram.types import Message
from utils.Logger import *
from utils.Config import Config
from utils.MongoClient import MongoDBClient
from helpers.fromatMessages import getMessage

config = Config()
MongoDBClient = MongoDBClient()


def errors(func: Callable) -> Callable:
async def decorator(client: Client, message: Message):
try:
return await func(client, message)
except Exception as e:
await message.reply(f"{type(e).__name__}: {e}")

return decorator


def hasRequiredPermission(user):
try:
required = ['can_post_messages', 'can_edit_messages', 'can_invite_users',
'can_delete_messages', 'can_manage_voice_chats', 'can_promote_members']
not_present = []
for _ in required:
if hasattr(user, _) is not True or user[_] is False:
not_present.append(_)
return len(not_present) == 0
except Exception as ex:
logWarning(f"Error while checking for required permission : {ex}")
return False


async def delayDelete(message, delay):
try:
await asyncio.sleep(delay)
await message.delete()
except Exception as ex:
logException("Error while deleteing message : {message} , {ex}", True)


def chat_allowed(func: Callable) -> Callable:
async def decorator(client: Client, message: Message):
try:
ALLOWED_CHAT_TYPES = config.get("ALLOWED_CHAT_TYPES")
current_client = config.fetchClient(message.chat.id)

# if the user is from a disallowed chat type show error message
if message.chat.type not in ALLOWED_CHAT_TYPES:
logInfo(
f"user not allowed to perform action in chat : {message.chat.id} , {message.chat.type}")

# if this is a provate message then add to database
if message.chat and message.chat.type == "private":
document = {"chat_id": message.chat.id, "type": "private", "username": message.chat.username if hasattr(message.chat, 'username') else "", "first_name": message.chat.first_name if hasattr(message.chat, 'first_name') else "",
"last_name": message.chat.last_name if hasattr(message.chat, 'last_name') else "", "updated_at": datetime.now()}
MongoDBClient.add_tgcalls_users(document)
msg, kbd = getMessage(message, 'private-chat')
return await client.send_message(message.chat.id, f"{msg}", disable_web_page_preview=True, reply_markup=kbd)
if current_client in [None, []]:
logInfo(f"Added a new client in db => {message.chat.id}")
document = {"chat_id": message.chat.id, "type": message.chat.type, "username": message.chat.username if hasattr(message.chat, 'username') else "", "title": message.chat.title if hasattr(message.chat, 'title') else "",
"permissions": {}, "updated_at": datetime.now(), 'admins': [], 'active': True, 'remove_messages': -1, 'admin_mode': False}
MongoDBClient.add_tgcalls_chats(document)
current_client = config.fetchClient(message.chat.id)

if current_client.get('active') is not True:
logInfo(
f"chat not allowed to perform action : {message.chat.id} , {message.chat.type}")

msg, kbd = getMessage(message, 'chat-not-allowed')
return await client.send_message(message.chat.id, f"{msg}", disable_web_page_preview=True, reply_markup=kbd)

return await func(client, message, current_client)
except Exception as ex:
logException(f"Error in chat_allowed : {ex}", True)
return decorator


def admin_check(func: Callable) -> Callable:
async def decorator(client: Client, message: Message, current_client=None):
try:
chat_id = message.chat.id
admins = config.getAdminForChat(chat_id)
if message.from_user is None or message.from_user.id in [a['chat_id'] for a in admins]:
current_client = config.fetchClient(message.chat.id)
return await func(client, message, current_client)
else:
logWarning(
f"Action blocked as user is not admin : {message.from_user} in chat {chat_id}, current_chat admins : {admins}")
m = await client.send_message(message.chat.id, f"**__This action can be performed only by admins.__**")
await delayDelete(m, 10)
except Exception as ex:
logException(f"Error in admin_check : {ex}", True)
return decorator


def admin_mode_check(func: Callable) -> Callable:
async def decorator(client: Client, message: Message, current_client):
try:
chat_id = message.chat.id
if current_client is not None and current_client.get('admin_mode') is not True:
return await func(client, message, current_client)
admins = config.getAdminForChat(chat_id)
if message.from_user is None or message.from_user.id in [a['chat_id'] for a in admins]:
return await func(client, message, current_client)

logWarning(
f"Action blocked as user is not admin and admin_mode is on : {message.from_user} , current_chat admins : {admins}")
msg = f"**❌ Admin mode is on in chat and your are not an admin[ Bot Admin ].**"
msg = msg + f"\n\n__Ask the admin to add you as admin using /auth command or disable the admin mode.__"
m = await client.send_message(message.chat.id, f"{msg}")
await delayDelete(m, 10)
except Exception as ex:
logException(f"Error in admin_mode_check : {ex}", True)
return decorator
86 changes: 86 additions & 0 deletions helpers/fromatMessages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from utils.Logger import *
from utils.Config import Config

Config = Config()


def getMessage(message, action):
try:
ALLOWED_CHAT_TYPES = config.get("ALLOWED_CHAT_TYPES")

if action == "private-chat":
send_message = f"**Hi 🎵 {message.chat.first_name if hasattr(message.chat, 'first_name') else 'User'}**"
send_message = send_message + \
f"\n\n**[Voice Chat Music Player]({config.get('BOT_URL')})** is a [SkTechHub Product]({config.get('PARENT_URL')})."
send_message = send_message + \
f"\n__It is designed to play, as simple as possible, music in your groups through the **new voice chats** introduced by Telegram.__"
send_message = send_message + \
f"\n\n**So why wait 🌀 add the bot to a group and get started 🎧**"
return send_message, getReplyKeyBoard(message, action)

elif action == "help-msg":
helpMessage = f"**VoiceChat Music Player (b1.5)**\n__Currently Available in **limited groups**.__"
helpMessage = helpMessage + \
f"\n\n• **/play song name/song url : ** __Start a song / add to queue.__"
helpMessage = helpMessage + f"\n• **/skip : ** __Skip to the next song in queue.__"
helpMessage = helpMessage + f"\n• **/stop : ** __Stop the playback.__"
helpMessage = helpMessage + \
f"\n• **/refreshadmins : ** __Refreshes the admin list.__"
helpMessage = helpMessage + \
f"\n• **/auth : ** __Adds the user in reply to the message as admin.__"
helpMessage = helpMessage + \
f"\n• **/unauth : ** __Removes the user in reply to the message as admin.__"
helpMessage = helpMessage + \
f"\n• **/listadmins : ** __Lists the users assigned as admins for the bot.__"
helpMessage = helpMessage + \
f"\n• **/adminmode on|off : ** __Turning this on makes the bot actions available only to bot admins.__"
helpMessage = helpMessage + \
f"\n• **/loop [2-5]|off : ** __Loop the playback [x] times(x is between 2-5) / Turn off the loop playback.__"
helpMessage = helpMessage + f"\n\n**__For any issues contact @voicechatsupport__**"
return helpMessage, getReplyKeyBoard(message, action)

elif action == "chat-not-allowed":
send_message = f"**😖 Sorry but this chat is not yet allowed to access the service. You can always check the demo in [Support Group]({config.get('SUPPORT_GROUP')}).**"
send_message = send_message + \
f"\n\n**Why ❓**\n1. __This service is still in testing phase and we are allowing access on an invite basis. To get access contact [Support Group]({config.get('SUPPORT_GROUP')}) __"
send_message = send_message + \
f"\n2. __Or your account has been banned. Contact [Support Group]({config.get('SUPPORT_GROUP')}).__"

return send_message, getReplyKeyBoard(message, action)

elif action == "start-voice-chat":
send_message = f"**Please start a voice chat and then send the command again**"
send_message = send_message + \
f"\n**1.** __To start a group chat, you can head over to your group’s description page.__"
send_message = send_message + \
f"\n**2.** __Then tap the three-dot button next to Mute and Search start a Voice Chat.__"
return send_message, getReplyKeyBoard(message, action)

except Exception as ex:
logException(f"**__Error : {ex}__**", True)


def getReplyKeyBoard(message, action):
try:
if action == "private-chat":
keyboard = InlineKeyboardMarkup(
[
[
InlineKeyboardButton(
"➕ Add the bot to Group ➕", url=f"{config.get('BOT_URL')}?startgroup=bot"),
],
[
InlineKeyboardButton(
"👥 Support Group", url=f"{config.get('SUPPORT_GROUP')}"),

InlineKeyboardButton(
"🌐 Website", url=f"{config.get('PARENT_URL')}"),
],

]
)
return keyboard
return None
except Exception as ex:
logException(f"**__Error : {ex}__**", True)
8 changes: 8 additions & 0 deletions helpers/queues/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from helpers.queues.queues import (
clear,
get,
is_empty,
put,
task_done,
size
)
Binary file added helpers/queues/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file added helpers/queues/__pycache__/queues.cpython-38.pyc
Binary file not shown.
Loading

0 comments on commit 5886bbc

Please sign in to comment.