├── .dockerignore ├── .github └── workflows │ ├── main.yml │ └── python-app.yml ├── .gitignore ├── Dockerfile ├── GeezProject ├── __main__.py ├── config.py ├── function │ ├── __init__.py │ └── admins.py ├── helpers │ ├── __init__.py │ ├── admins.py │ ├── channelmusic.py │ ├── decorators.py │ ├── errors.py │ ├── filters.py │ └── gets.py ├── modules │ ├── __init__.py │ ├── admins.py │ ├── channeladmin.py │ ├── channelplay.py │ ├── chat_member_updated.py │ ├── gcast.py │ ├── inline.py │ ├── msg.py │ ├── play.py │ ├── pmpermit.py │ ├── private.py │ ├── song.py │ ├── userbotjoin.py │ └── ytsearch.py └── services │ ├── __init__.py │ ├── callsmusic │ ├── __init__.py │ └── callsmusic.py │ ├── converter │ ├── __init__.py │ └── converter.py │ ├── downloaders │ ├── __init__.py │ └── youtube.py │ └── queues │ ├── __init__.py │ └── queues.py ├── LICENSE ├── README.md ├── app.json ├── etc └── font.otf ├── example.env ├── heroku.yml ├── requirements.txt └── str.py /.dockerignore: -------------------------------------------------------------------------------- 1 | LICENSE 2 | README.md 3 | 4 | .env 5 | .gitignore 6 | 7 | .git/ 8 | __pycache__/ 9 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the Geez-Userbot branch 8 | push: 9 | branches: [ master] 10 | pull_request: 11 | branches: [ master] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | # Runs a single command using the runners shell 29 | - name: Run a one-line script 30 | run: echo Hello, world! 31 | 32 | # Runs a set of commands using the runners shell 33 | - name: Run a multi-line script 34 | run: | 35 | echo Add other actions to build, 36 | echo test, and deploy your project. 37 | -------------------------------------------------------------------------------- /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | name: FailCheck 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | max-parallel: 5 11 | matrix: 12 | python-version: [3.9] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | sudo apt-get install libpq-dev 23 | python -m pip install --upgrade pip 24 | pip install -r requirements.txt 25 | pip install flake8 flake8-print flake8-quotes 26 | - name: Check for showstoppers 27 | run: | 28 | flake8 . --count --select=E999 --show-source --statistics 29 | shellcheck: 30 | 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v1 35 | - name: Check for install script errors 36 | uses: ludeeus/action-shellcheck@0.1.0 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.session 2 | *.session-journal 3 | .env 4 | __pycache__ 5 | .idea 6 | .vscode 7 | raw_files 8 | downloads -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:latest 2 | FROM python:3.9.6-slim-buster 3 | RUN apt update && apt upgrade -y 4 | RUN apt install git curl python3-pip ffmpeg -y 5 | RUN pip3 install -U pip 6 | RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - 7 | RUN apt-get install -y nodejs 8 | RUN npm i -g npm 9 | RUN mkdir /app/ 10 | WORKDIR /app/ 11 | COPY . /app/ 12 | RUN pip3 install -U -r requirements.txt 13 | CMD python3 -m GeezProject 14 | -------------------------------------------------------------------------------- /GeezProject/__main__.py: -------------------------------------------------------------------------------- 1 | # DaisyXMusic (Telegram bot project) 2 | # Copyright (C) 2021 Inuka Asith & Rojserbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | import requests 19 | from pyrogram import Client as Bot 20 | 21 | from GeezProject.config import API_HASH, API_ID, BG_IMAGE, BOT_TOKEN 22 | from GeezProject.services.callsmusic import run 23 | 24 | response = requests.get(BG_IMAGE) 25 | with open("./etc/foreground.png", "wb") as file: 26 | file.write(response.content) 27 | bot = Bot( 28 | ":memory:", 29 | API_ID, 30 | API_HASH, 31 | bot_token=BOT_TOKEN, 32 | plugins=dict(root="GeezProject.modules"), 33 | ) 34 | 35 | bot.start() 36 | run() 37 | -------------------------------------------------------------------------------- /GeezProject/config.py: -------------------------------------------------------------------------------- 1 | # DAISYXMUSIC- Telegram bot project 2 | # Copyright (C) 2021 Roj Serbest 3 | # Copyright (C) 2021 Inuka Asith 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | # Modified by Inukaasith 17 | 18 | import os 19 | from os import getenv 20 | 21 | from dotenv import load_dotenv 22 | 23 | if os.path.exists("local.env"): 24 | load_dotenv("local.env") 25 | 26 | que = {} 27 | SESSION_NAME = getenv("SESSION_NAME", "session") 28 | BOT_TOKEN = getenv("BOT_TOKEN") 29 | BOT_NAME = getenv("BOT_NAME") 30 | UPDATES_CHANNEL = getenv("UPDATES_CHANNEL", "GeezProjects") 31 | BG_IMAGE = getenv("BG_IMAGE", "https://telegra.ph/file/22d1625c6910bdb0dac66.png") 32 | admins = {} 33 | API_ID = int(getenv("API_ID")) 34 | API_HASH = getenv("API_HASH") 35 | BOT_USERNAME = getenv("BOT_USERNAME") 36 | ASSISTANT_NAME = getenv("ASSISTANT_NAME", "GeezMusicBot") 37 | SUPPORT_GROUP = getenv("SUPPORT_GROUP", "GeezSupportGroup") 38 | PROJECT_NAME = getenv("PROJECT_NAME", "Geez Music Project") 39 | OWNER = getenv("OWNER", "@VckyouuBitch") 40 | SOURCE_CODE = getenv("SOURCE_CODE", "github.com/Vckyou/Geez-MusicProject") 41 | DURATION_LIMIT = int(getenv("DURATION_LIMIT", "15")) 42 | ARQ_API_KEY = getenv("ARQ_API_KEY", None) 43 | PMPERMIT = getenv("PMPERMIT", None) 44 | LOG_GRP = getenv("LOG_GRP", None) 45 | COMMAND_PREFIXES = list(getenv("COMMAND_PREFIXES", "/ !").split()) 46 | 47 | SUDO_USERS = list(map(int, getenv("SUDO_USERS").split())) 48 | -------------------------------------------------------------------------------- /GeezProject/function/__init__.py: -------------------------------------------------------------------------------- 1 | from GeezProject.function.admins import admins, get, set 2 | 3 | __all__ = ["set", "get", "admins"] 4 | -------------------------------------------------------------------------------- /GeezProject/function/admins.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Union 2 | 3 | admins: Dict[int, List[int]] = {} 4 | 5 | 6 | def set(chat_id: int, admins_: List[int]): 7 | admins[chat_id] = admins_ 8 | 9 | 10 | def get(chat_id: int) -> Union[List[int], bool]: 11 | if chat_id in admins: 12 | return admins[chat_id] 13 | 14 | return False 15 | -------------------------------------------------------------------------------- /GeezProject/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /GeezProject/helpers/admins.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from typing import List 19 | 20 | from pyrogram.types import Chat 21 | 22 | from GeezProject.function.admins import get as gett 23 | from GeezProject.function.admins import set 24 | 25 | 26 | async def get_administrators(chat: Chat) -> List[int]: 27 | get = gett(chat.id) 28 | 29 | if get: 30 | return get 31 | else: 32 | administrators = await chat.get_members(filter="administrators") 33 | to_set = [] 34 | 35 | for administrator in administrators: 36 | if administrator.can_manage_voice_chats: 37 | to_set.append(administrator.user.id) 38 | 39 | set(chat.id, to_set) 40 | return await get_administrators(chat) 41 | -------------------------------------------------------------------------------- /GeezProject/helpers/channelmusic.py: -------------------------------------------------------------------------------- 1 | from pyrogram.types import Chat 2 | 3 | 4 | def get_chat_id(chat: Chat): 5 | if chat.title.startswith("Channel Music: ") and chat.title[16:].isnumeric(): 6 | return int(chat.title[15:]) 7 | return chat.id 8 | -------------------------------------------------------------------------------- /GeezProject/helpers/decorators.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from typing import Callable 19 | 20 | from pyrogram import Client 21 | from pyrogram.types import Message 22 | 23 | from GeezProject.config import SUDO_USERS 24 | from GeezProject.helpers.admins import get_administrators 25 | 26 | 27 | def errors(func: Callable) -> Callable: 28 | async def decorator(client: Client, message: Message): 29 | try: 30 | return await func(client, message) 31 | except Exception as e: 32 | await message.reply(f"{type(e).__name__}: {e}") 33 | 34 | return decorator 35 | 36 | 37 | def authorized_users_only(func: Callable) -> Callable: 38 | async def decorator(client: Client, message: Message): 39 | if message.from_user.id in SUDO_USERS: 40 | return await func(client, message) 41 | 42 | administrators = await get_administrators(message.chat) 43 | 44 | for administrator in administrators: 45 | if administrator == message.from_user.id: 46 | return await func(client, message) 47 | 48 | return decorator 49 | -------------------------------------------------------------------------------- /GeezProject/helpers/errors.py: -------------------------------------------------------------------------------- 1 | class DurationLimitError(Exception): 2 | pass 3 | 4 | 5 | class FFmpegReturnCodeError(Exception): 6 | pass 7 | -------------------------------------------------------------------------------- /GeezProject/helpers/filters.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from typing import List, Union 19 | 20 | from pyrogram import filters 21 | 22 | from GeezProject.config import COMMAND_PREFIXES 23 | 24 | other_filters = filters.group & ~filters.edited & ~filters.via_bot & ~filters.forwarded 25 | other_filters2 = ( 26 | filters.private & ~filters.edited & ~filters.via_bot & ~filters.forwarded 27 | ) 28 | 29 | 30 | def command(commands: Union[str, List[str]]): 31 | return filters.command(commands, COMMAND_PREFIXES) 32 | -------------------------------------------------------------------------------- /GeezProject/helpers/gets.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from typing import Union 19 | 20 | from pyrogram.types import Audio, Message, Voice 21 | 22 | 23 | def get_url(message_1: Message) -> Union[str, None]: 24 | messages = [message_1] 25 | 26 | if message_1.reply_to_message: 27 | messages.append(message_1.reply_to_message) 28 | 29 | text = "" 30 | offset = None 31 | length = None 32 | 33 | for message in messages: 34 | if offset: 35 | break 36 | 37 | if message.entities: 38 | for entity in message.entities: 39 | if entity.type == "url": 40 | text = message.text or message.caption 41 | offset, length = entity.offset, entity.length 42 | break 43 | 44 | if offset in (None,): 45 | return None 46 | 47 | return text[offset : offset + length] 48 | 49 | 50 | def get_file_name(audio: Union[Audio, Voice]): 51 | return f'{audio.file_unique_id}.{audio.file_name.split(".")[-1] if not isinstance(audio, Voice) else "ogg"}' 52 | -------------------------------------------------------------------------------- /GeezProject/modules/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /GeezProject/modules/admins.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 VckyouuBitch 3 | # Recode By VICKY From Geez-MusicProject 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | from asyncio import QueueEmpty 20 | from GeezProject.config import que 21 | from pyrogram import Client, filters 22 | from pyrogram.types import Message 23 | 24 | from GeezProject.function.admins import set 25 | from GeezProject.helpers.channelmusic import get_chat_id 26 | from GeezProject.helpers.decorators import authorized_users_only, errors 27 | from GeezProject.helpers.filters import command, other_filters 28 | from GeezProject.services.callsmusic import callsmusic 29 | from GeezProject.services.queues import queues 30 | 31 | 32 | @Client.on_message(filters.command("adminreset")) 33 | async def update_admin(client, message: Message): 34 | chat_id = get_chat_id(message.chat) 35 | set( 36 | chat_id, 37 | [ 38 | member.user 39 | for member in await message.chat.get_members(filter="administrators") 40 | ], 41 | ) 42 | await message.reply_text("❇️ Admin cache refreshed!") 43 | 44 | 45 | @Client.on_message(command("pause") & other_filters) 46 | @errors 47 | @authorized_users_only 48 | async def pause(_, message: Message): 49 | chat_id = get_chat_id(message.chat) 50 | if (chat_id not in callsmusic.pytgcalls.active_calls) or ( 51 | callsmusic.pytgcalls.active_calls[chat_id] == "paused" 52 | ): 53 | await message.reply_text("❗ Nothing is playing!") 54 | else: 55 | callsmusic.pytgcalls.pause_stream(chat_id) 56 | await message.reply_text("▶️ Paused!") 57 | 58 | 59 | @Client.on_message(command("resume") & other_filters) 60 | @errors 61 | @authorized_users_only 62 | async def resume(_, message: Message): 63 | chat_id = get_chat_id(message.chat) 64 | if (chat_id not in callsmusic.pytgcalls.active_calls) or ( 65 | callsmusic.pytgcalls.active_calls[chat_id] == "playing" 66 | ): 67 | await message.reply_text("❗ Nothing is paused!") 68 | else: 69 | callsmusic.pytgcalls.resume_stream(chat_id) 70 | await message.reply_text("⏸ Resumed!") 71 | 72 | 73 | @Client.on_message(command("end") & other_filters) 74 | @errors 75 | @authorized_users_only 76 | async def stop(_, message: Message): 77 | chat_id = get_chat_id(message.chat) 78 | if chat_id not in callsmusic.pytgcalls.active_calls: 79 | await message.reply_text("❗ Nothing is streaming!") 80 | else: 81 | try: 82 | callsmusic.queues.clear(chat_id) 83 | except QueueEmpty: 84 | pass 85 | 86 | callsmusic.pytgcalls.leave_group_call(chat_id) 87 | await message.reply_text("❌ Stopped streaming!") 88 | 89 | 90 | @Client.on_message(command("skip") & other_filters) 91 | @errors 92 | @authorized_users_only 93 | async def skip(_, message: Message): 94 | global que 95 | chat_id = get_chat_id(message.chat) 96 | if chat_id not in callsmusic.pytgcalls.active_calls: 97 | await message.reply_text("❗ Nothing is playing to skip!") 98 | else: 99 | callsmusic.queues.task_done(chat_id) 100 | 101 | if callsmusic.queues.is_empty(chat_id): 102 | callsmusic.pytgcalls.leave_group_call(chat_id) 103 | else: 104 | callsmusic.pytgcalls.change_stream( 105 | chat_id, callsmusic.queues.get(chat_id)["file"] 106 | ) 107 | 108 | qeue = que.get(chat_id) 109 | if qeue: 110 | skip = qeue.pop(0) 111 | if not qeue: 112 | return 113 | await message.reply_text(f"- Skipped **{skip[0]}**\n- Now Playing **{qeue[0][0]}**") 114 | 115 | 116 | @Client.on_message(filters.command("admincache")) 117 | @errors 118 | async def admincache(client, message: Message): 119 | set( 120 | message.chat.id, 121 | [ 122 | member.user 123 | for member in await message.chat.get_members(filter="administrators") 124 | ], 125 | ) 126 | await message.reply_text("✅️ **Daftar admin** telah **diperbarui**") 127 | -------------------------------------------------------------------------------- /GeezProject/modules/channeladmin.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from asyncio import QueueEmpty 19 | from GeezProject.config import que 20 | from pyrogram import Client, filters 21 | from pyrogram.types import Message 22 | 23 | from GeezProject.function.admins import set 24 | from GeezProject.helpers.channelmusic import get_chat_id 25 | from GeezProject.helpers.decorators import authorized_users_only, errors 26 | from GeezProject.helpers.filters import command, other_filters 27 | from GeezProject.services.callsmusic import callsmusic 28 | from GeezProject.services.queues import queues 29 | 30 | 31 | @Client.on_message(filters.command(["channelpause","cpause"]) & filters.group & ~filters.edited) 32 | @errors 33 | @authorized_users_only 34 | async def pause(_, message: Message): 35 | try: 36 | conchat = await _.get_chat(message.chat.id) 37 | conid = conchat.linked_chat.id 38 | chid = conid 39 | except: 40 | await message.reply("Is chat even linked") 41 | return 42 | chat_id = chid 43 | if (chat_id not in callsmusic.pytgcalls.active_calls) or ( 44 | callsmusic.pytgcalls.active_calls[chat_id] == "paused" 45 | ): 46 | await message.reply_text("❗ Nothing is playing!") 47 | else: 48 | callsmusic.pytgcalls.pause_stream(chat_id) 49 | await message.reply_text("▶️ Paused!") 50 | 51 | 52 | @Client.on_message(filters.command(["channelresume","cresume"]) & filters.group & ~filters.edited) 53 | @errors 54 | @authorized_users_only 55 | async def resume(_, message: Message): 56 | try: 57 | conchat = await _.get_chat(message.chat.id) 58 | conid = conchat.linked_chat.id 59 | chid = conid 60 | except: 61 | await message.reply("Is chat even linked") 62 | return 63 | chat_id = chid 64 | if (chat_id not in callsmusic.pytgcalls.active_calls) or ( 65 | callsmusic.pytgcalls.active_calls[chat_id] == "playing" 66 | ): 67 | await message.reply_text("❗ Nothing is paused!") 68 | else: 69 | callsmusic.pytgcalls.resume_stream(chat_id) 70 | await message.reply_text("⏸ Resumed!") 71 | 72 | 73 | @Client.on_message(filters.command(["channelend","cend"]) & filters.group & ~filters.edited) 74 | @errors 75 | @authorized_users_only 76 | async def stop(_, message: Message): 77 | try: 78 | conchat = await _.get_chat(message.chat.id) 79 | conid = conchat.linked_chat.id 80 | chid = conid 81 | except: 82 | await message.reply("Is chat even linked") 83 | return 84 | chat_id = chid 85 | if chat_id not in callsmusic.pytgcalls.active_calls: 86 | await message.reply_text("❗ Nothing is streaming!") 87 | else: 88 | try: 89 | callsmusic.queues.clear(chat_id) 90 | except QueueEmpty: 91 | pass 92 | 93 | callsmusic.pytgcalls.leave_group_call(chat_id) 94 | await message.reply_text("❌ Stopped streaming!") 95 | 96 | 97 | @Client.on_message(filters.command(["channelskip","cskip"]) & filters.group & ~filters.edited) 98 | @errors 99 | @authorized_users_only 100 | async def skip(_, message: Message): 101 | global que 102 | try: 103 | conchat = await _.get_chat(message.chat.id) 104 | conid = conchat.linked_chat.id 105 | chid = conid 106 | except: 107 | await message.reply("Is chat even linked") 108 | return 109 | chat_id = chid 110 | if chat_id not in callsmusic.pytgcalls.active_calls: 111 | await message.reply_text("❗ Nothing is playing to skip!") 112 | else: 113 | callsmusic.queues.task_done(chat_id) 114 | 115 | if callsmusic.queues.is_empty(chat_id): 116 | callsmusic.pytgcalls.leave_group_call(chat_id) 117 | else: 118 | callsmusic.pytgcalls.change_stream( 119 | chat_id, callsmusic.queues.get(chat_id)["file"] 120 | ) 121 | 122 | qeue = que.get(chat_id) 123 | if qeue: 124 | skip = qeue.pop(0) 125 | if not qeue: 126 | return 127 | await message.reply_text(f"- Skipped **{skip[0]}**\n- Now Playing **{qeue[0][0]}**") 128 | 129 | 130 | @Client.on_message(filters.command("channeladmincache")) 131 | @errors 132 | async def admincache(client, message: Message): 133 | try: 134 | conchat = await client.get_chat(message.chat.id) 135 | conid = conchat.linked_chat.id 136 | chid = conid 137 | except: 138 | await message.reply("Is chat even linked") 139 | return 140 | set( 141 | chid, 142 | [ 143 | member.user 144 | for member in await conchat.linked_chat.get_members(filter="administrators") 145 | ], 146 | ) 147 | await message.reply_text("❇️ Admin cache refreshed!") 148 | -------------------------------------------------------------------------------- /GeezProject/modules/channelplay.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project) 2 | # Copyright (C) 2021 Inukaasith 3 | # Copyright (C) 2021 TheHamkerCat (Python_ARQ) 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | import json 19 | import os 20 | from os import path 21 | from typing import Callable 22 | 23 | import aiofiles 24 | import aiohttp 25 | import ffmpeg 26 | import requests 27 | import wget 28 | from PIL import Image, ImageDraw, ImageFont 29 | from pyrogram import Client, filters 30 | from pyrogram.errors import UserAlreadyParticipant 31 | from pyrogram.types import Voice 32 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message 33 | from Python_ARQ import ARQ 34 | from youtube_search import YoutubeSearch 35 | from GeezProject.modules.play import generate_cover 36 | from GeezProject.modules.play import arq 37 | from GeezProject.modules.play import cb_admin_check 38 | from GeezProject.modules.play import transcode 39 | from GeezProject.modules.play import convert_seconds 40 | from GeezProject.modules.play import time_to_seconds 41 | from GeezProject.modules.play import changeImageSize 42 | from GeezProject.config import BOT_NAME as bn 43 | from GeezProject.config import DURATION_LIMIT 44 | from GeezProject.config import UPDATES_CHANNEL as updateschannel 45 | from GeezProject.config import que 46 | from GeezProject.function.admins import admins as a 47 | from GeezProject.helpers.errors import DurationLimitError 48 | from GeezProject.helpers.decorators import errors 49 | from GeezProject.helpers.admins import get_administrators 50 | from GeezProject.helpers.channelmusic import get_chat_id 51 | from GeezProject.helpers.decorators import authorized_users_only 52 | from GeezProject.helpers.filters import command, other_filters 53 | from GeezProject.helpers.gets import get_file_name 54 | from GeezProject.services.callsmusic import callsmusic 55 | from GeezProject.services.callsmusic.callsmusic import client as USER 56 | from GeezProject.services.converter.converter import convert 57 | from GeezProject.services.downloaders import youtube 58 | from GeezProject.services.queues import queues 59 | 60 | chat_id = None 61 | 62 | 63 | 64 | @Client.on_message(filters.command(["channelplaylist","cplaylist"]) & filters.group & ~filters.edited) 65 | async def playlist(client, message): 66 | try: 67 | lel = await client.get_chat(message.chat.id) 68 | lol = lel.linked_chat.id 69 | except: 70 | message.reply("Is this cat even linked?") 71 | return 72 | global que 73 | queue = que.get(lol) 74 | if not queue: 75 | await message.reply_text("Player is idle") 76 | temp = [] 77 | for t in queue: 78 | temp.append(t) 79 | now_playing = temp[0][0] 80 | by = temp[0][1].mention(style="md") 81 | msg = "**Now Playing** in {}".format(lel.linked_chat.title) 82 | msg += "\n- " + now_playing 83 | msg += "\n- Req by " + by 84 | temp.pop(0) 85 | if temp: 86 | msg += "\n\n" 87 | msg += "**Queue**" 88 | for song in temp: 89 | name = song[0] 90 | usr = song[1].mention(style="md") 91 | msg += f"\n- {name}" 92 | msg += f"\n- Req by {usr}\n" 93 | await message.reply_text(msg) 94 | 95 | 96 | # ============================= Settings ========================================= 97 | 98 | 99 | def updated_stats(chat, queue, vol=100): 100 | if chat.id in callsmusic.pytgcalls.active_calls: 101 | # if chat.id in active_chats: 102 | stats = "Settings of **{}**".format(chat.title) 103 | if len(que) > 0: 104 | stats += "\n\n" 105 | stats += "Volume : {}%\n".format(vol) 106 | stats += "Songs in queue : `{}`\n".format(len(que)) 107 | stats += "Now Playing : **{}**\n".format(queue[0][0]) 108 | stats += "Requested by : {}".format(queue[0][1].mention) 109 | else: 110 | stats = None 111 | return stats 112 | 113 | 114 | def r_ply(type_): 115 | if type_ == "play": 116 | pass 117 | else: 118 | pass 119 | mar = InlineKeyboardMarkup( 120 | [ 121 | [ 122 | InlineKeyboardButton("⏹", "cleave"), 123 | InlineKeyboardButton("⏸", "cpuse"), 124 | InlineKeyboardButton("▶️", "cresume"), 125 | InlineKeyboardButton("⏭", "cskip"), 126 | ], 127 | [ 128 | InlineKeyboardButton("Playlist 📖", "cplaylist"), 129 | ], 130 | [InlineKeyboardButton("❌ Close", "ccls")], 131 | ] 132 | ) 133 | return mar 134 | 135 | 136 | @Client.on_message(filters.command(["channelcurrent","ccurrent"]) & filters.group & ~filters.edited) 137 | async def ee(client, message): 138 | try: 139 | lel = await client.get_chat(message.chat.id) 140 | lol = lel.linked_chat.id 141 | conv = lel.linked_chat 142 | except: 143 | await message.reply("Is chat even linked") 144 | return 145 | queue = que.get(lol) 146 | stats = updated_stats(conv, queue) 147 | if stats: 148 | await message.reply(stats) 149 | else: 150 | await message.reply("No VC instances running in this chat") 151 | 152 | 153 | @Client.on_message(filters.command(["channelplayer","cplayer"]) & filters.group & ~filters.edited) 154 | @authorized_users_only 155 | async def settings(client, message): 156 | playing = None 157 | try: 158 | lel = await client.get_chat(message.chat.id) 159 | lol = lel.linked_chat.id 160 | conv = lel.linked_chat 161 | except: 162 | await message.reply("Is chat even linked") 163 | return 164 | queue = que.get(lol) 165 | stats = updated_stats(conv, queue) 166 | if stats: 167 | if playing: 168 | await message.reply(stats, reply_markup=r_ply("pause")) 169 | 170 | else: 171 | await message.reply(stats, reply_markup=r_ply("play")) 172 | else: 173 | await message.reply("No VC instances running in this chat") 174 | 175 | 176 | @Client.on_callback_query(filters.regex(pattern=r"^(cplaylist)$")) 177 | async def p_cb(b, cb): 178 | global que 179 | try: 180 | lel = await client.get_chat(cb.message.chat.id) 181 | lol = lel.linked_chat.id 182 | conv = lel.linked_chat 183 | except: 184 | return 185 | que.get(lol) 186 | type_ = cb.matches[0].group(1) 187 | cb.message.chat.id 188 | cb.message.chat 189 | cb.message.reply_markup.inline_keyboard[1][0].callback_data 190 | if type_ == "playlist": 191 | queue = que.get(lol) 192 | if not queue: 193 | await cb.message.edit("Player is idle") 194 | temp = [] 195 | for t in queue: 196 | temp.append(t) 197 | now_playing = temp[0][0] 198 | by = temp[0][1].mention(style="md") 199 | msg = "**Now Playing** in {}".format(conv.title) 200 | msg += "\n- " + now_playing 201 | msg += "\n- Req by " + by 202 | temp.pop(0) 203 | if temp: 204 | msg += "\n\n" 205 | msg += "**Queue**" 206 | for song in temp: 207 | name = song[0] 208 | usr = song[1].mention(style="md") 209 | msg += f"\n- {name}" 210 | msg += f"\n- Req by {usr}\n" 211 | await cb.message.edit(msg) 212 | 213 | 214 | @Client.on_callback_query( 215 | filters.regex(pattern=r"^(cplay|cpause|cskip|cleave|cpuse|cresume|cmenu|ccls)$") 216 | ) 217 | @cb_admin_check 218 | async def m_cb(b, cb): 219 | global que 220 | if ( 221 | cb.message.chat.title.startswith("Channel Music: ") 222 | and chat.title[14:].isnumeric() 223 | ): 224 | chet_id = int(chat.title[13:]) 225 | else: 226 | try: 227 | lel = await b.get_chat(cb.message.chat.id) 228 | lol = lel.linked_chat.id 229 | conv = lel.linked_chat 230 | chet_id = lol 231 | except: 232 | return 233 | qeue = que.get(chet_id) 234 | type_ = cb.matches[0].group(1) 235 | cb.message.chat.id 236 | m_chat = cb.message.chat 237 | 238 | 239 | the_data = cb.message.reply_markup.inline_keyboard[1][0].callback_data 240 | if type_ == "cpause": 241 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 242 | callsmusic.pytgcalls.active_calls[chet_id] == "paused" 243 | ): 244 | await cb.answer("Chat is not connected!", show_alert=True) 245 | else: 246 | callsmusic.pytgcalls.pause_stream(chet_id) 247 | 248 | await cb.answer("Music Paused!") 249 | await cb.message.edit( 250 | updated_stats(conv, qeue), reply_markup=r_ply("play") 251 | ) 252 | 253 | elif type_ == "cplay": 254 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 255 | callsmusic.pytgcalls.active_calls[chet_id] == "playing" 256 | ): 257 | await cb.answer("Chat is not connected!", show_alert=True) 258 | else: 259 | callsmusic.pytgcalls.resume_stream(chet_id) 260 | await cb.answer("Music Resumed!") 261 | await cb.message.edit( 262 | updated_stats(conv, qeue), reply_markup=r_ply("pause") 263 | ) 264 | 265 | elif type_ == "cplaylist": 266 | queue = que.get(cb.message.chat.id) 267 | if not queue: 268 | await cb.message.edit("Player is idle") 269 | temp = [] 270 | for t in queue: 271 | temp.append(t) 272 | now_playing = temp[0][0] 273 | by = temp[0][1].mention(style="md") 274 | msg = "**Now Playing** in {}".format(cb.message.chat.title) 275 | msg += "\n- " + now_playing 276 | msg += "\n- Req by " + by 277 | temp.pop(0) 278 | if temp: 279 | msg += "\n\n" 280 | msg += "**Queue**" 281 | for song in temp: 282 | name = song[0] 283 | usr = song[1].mention(style="md") 284 | msg += f"\n- {name}" 285 | msg += f"\n- Req by {usr}\n" 286 | await cb.message.edit(msg) 287 | 288 | elif type_ == "cresume": 289 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 290 | callsmusic.pytgcalls.active_calls[chet_id] == "playing" 291 | ): 292 | await cb.answer("Chat is not connected or already playng", show_alert=True) 293 | else: 294 | callsmusic.pytgcalls.resume_stream(chet_id) 295 | await cb.answer("Music Resumed!") 296 | elif type_ == "cpuse": 297 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 298 | callsmusic.pytgcalls.active_calls[chet_id] == "paused" 299 | ): 300 | await cb.answer("Chat is not connected or already paused", show_alert=True) 301 | else: 302 | callsmusic.pytgcalls.pause_stream(chet_id) 303 | 304 | await cb.answer("Music Paused!") 305 | elif type_ == "ccls": 306 | await cb.answer("Closed menu") 307 | await cb.message.delete() 308 | 309 | elif type_ == "cmenu": 310 | stats = updated_stats(conv, qeue) 311 | await cb.answer("Menu opened") 312 | marr = InlineKeyboardMarkup( 313 | [ 314 | [ 315 | InlineKeyboardButton("⏹", "cleave"), 316 | InlineKeyboardButton("⏸", "cpuse"), 317 | InlineKeyboardButton("▶️", "cresume"), 318 | InlineKeyboardButton("⏭", "cskip"), 319 | ], 320 | [ 321 | InlineKeyboardButton("Playlist 📖", "cplaylist"), 322 | ], 323 | [InlineKeyboardButton("❌ Close", "ccls")], 324 | ] 325 | ) 326 | await cb.message.edit(stats, reply_markup=marr) 327 | elif type_ == "cskip": 328 | if qeue: 329 | qeue.pop(0) 330 | if chet_id not in callsmusic.pytgcalls.active_calls: 331 | await cb.answer("Chat is not connected!", show_alert=True) 332 | else: 333 | callsmusic.queues.task_done(chet_id) 334 | 335 | if callsmusic.queues.is_empty(chet_id): 336 | callsmusic.pytgcalls.leave_group_call(chet_id) 337 | 338 | await cb.message.edit("- No More Playlist..\n- Leaving VC!") 339 | else: 340 | callsmusic.pytgcalls.change_stream( 341 | chet_id, callsmusic.queues.get(chet_id)["file"] 342 | ) 343 | await cb.answer("Skipped") 344 | await cb.message.edit((m_chat, qeue), reply_markup=r_ply(the_data)) 345 | await cb.message.reply_text( 346 | f"- Skipped track\n- Now Playing **{qeue[0][0]}**" 347 | ) 348 | 349 | else: 350 | if chet_id in callsmusic.pytgcalls.active_calls: 351 | try: 352 | callsmusic.queues.clear(chet_id) 353 | except QueueEmpty: 354 | pass 355 | 356 | callsmusic.pytgcalls.leave_group_call(chet_id) 357 | await cb.message.edit("Successfully Left the Chat!") 358 | else: 359 | await cb.answer("Chat is not connected!", show_alert=True) 360 | 361 | 362 | @Client.on_message(filters.command(["channelplay","cplay"]) & filters.group & ~filters.edited) 363 | @authorized_users_only 364 | async def play(_, message: Message): 365 | global que 366 | lel = await message.reply("🔄 **Processing**") 367 | 368 | try: 369 | conchat = await _.get_chat(message.chat.id) 370 | conv = conchat.linked_chat 371 | conid = conchat.linked_chat.id 372 | chid = conid 373 | except: 374 | await message.reply("Is chat even linked") 375 | return 376 | try: 377 | administrators = await get_administrators(conv) 378 | except: 379 | await message.reply("Am I admin of Channel") 380 | try: 381 | user = await USER.get_me() 382 | except: 383 | user.first_name = "helper" 384 | usar = user 385 | wew = usar.id 386 | try: 387 | # chatdetails = await USER.get_chat(chid) 388 | await _.get_chat_member(chid, wew) 389 | except: 390 | for administrator in administrators: 391 | if administrator == message.from_user.id: 392 | if message.chat.title.startswith("Channel Music: "): 393 | await lel.edit( 394 | "Remember to add helper to your channel", 395 | ) 396 | pass 397 | 398 | try: 399 | invitelink = await _.export_chat_invite_link(chid) 400 | except: 401 | await lel.edit( 402 | "Add me as admin of yor channel first", 403 | ) 404 | return 405 | 406 | try: 407 | await USER.join_chat(invitelink) 408 | await lel.edit( 409 | "helper userbot joined your channel", 410 | ) 411 | 412 | except UserAlreadyParticipant: 413 | pass 414 | except Exception: 415 | # print(e) 416 | await lel.edit( 417 | f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your channel due to heavy requests for userbot! Make sure user is not banned in group." 418 | "\n\nOr manually add assistant to your Group and try again", 419 | ) 420 | try: 421 | await USER.get_chat(chid) 422 | # lmoa = await client.get_chat_member(chid,wew) 423 | except: 424 | await lel.edit( 425 | f" {user.first_name} Userbot not in this chat, Ask channel admin to send /play command for first time or add {user.first_name} manually" 426 | ) 427 | return 428 | message.from_user.id 429 | text_links = None 430 | message.from_user.first_name 431 | await lel.edit("🔎 **Finding**") 432 | message.from_user.id 433 | user_id = message.from_user.id 434 | message.from_user.first_name 435 | user_name = message.from_user.first_name 436 | rpk = "[" + user_name + "](tg://user?id=" + str(user_id) + ")" 437 | if message.reply_to_message: 438 | entities = [] 439 | toxt = message.reply_to_message.text or message.reply_to_message.caption 440 | if message.reply_to_message.entities: 441 | entities = message.reply_to_message.entities + entities 442 | elif message.reply_to_message.caption_entities: 443 | entities = message.reply_to_message.entities + entities 444 | urls = [entity for entity in entities if entity.type == 'url'] 445 | text_links = [ 446 | entity for entity in entities if entity.type == 'text_link' 447 | ] 448 | else: 449 | urls=None 450 | if text_links: 451 | urls = True 452 | audio = ( 453 | (message.reply_to_message.audio or message.reply_to_message.voice) 454 | if message.reply_to_message 455 | else None 456 | ) 457 | if audio: 458 | if round(audio.duration / 60) > DURATION_LIMIT: 459 | raise DurationLimitError( 460 | f"❌ Videos longer than {DURATION_LIMIT} minute(s) aren't allowed to play!" 461 | ) 462 | keyboard = InlineKeyboardMarkup( 463 | [ 464 | [ 465 | InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), 466 | InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), 467 | ], 468 | [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], 469 | ] 470 | ) 471 | file_name = get_file_name(audio) 472 | title = file_name 473 | thumb_name = "https://telegra.ph/file/f6086f8909fbfeb0844f2.png" 474 | thumbnail = thumb_name 475 | duration = round(audio.duration / 60) 476 | views = "Locally added" 477 | requested_by = message.from_user.first_name 478 | await generate_cover(requested_by, title, views, duration, thumbnail) 479 | file_path = await convert( 480 | (await message.reply_to_message.download(file_name)) 481 | if not path.isfile(path.join("downloads", file_name)) 482 | else file_name 483 | ) 484 | elif urls: 485 | query = toxt 486 | await lel.edit("🎵 **Processing**") 487 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 488 | try: 489 | results = YoutubeSearch(query, max_results=1).to_dict() 490 | url = f"https://youtube.com{results[0]['url_suffix']}" 491 | # print(results) 492 | title = results[0]["title"][:40] 493 | thumbnail = results[0]["thumbnails"][0] 494 | thumb_name = f"thumb{title}.jpg" 495 | thumb = requests.get(thumbnail, allow_redirects=True) 496 | open(thumb_name, "wb").write(thumb.content) 497 | duration = results[0]["duration"] 498 | results[0]["url_suffix"] 499 | views = results[0]["views"] 500 | 501 | except Exception as e: 502 | await lel.edit( 503 | "Song not found.Try another song or maybe spell it properly." 504 | ) 505 | print(str(e)) 506 | return 507 | dlurl = url 508 | dlurl=dlurl.replace("youtube","youtubepp") 509 | keyboard = InlineKeyboardMarkup( 510 | [ 511 | [ 512 | InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), 513 | InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), 514 | ], 515 | [ 516 | InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), 517 | InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), 518 | ], 519 | [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], 520 | ] 521 | ) 522 | requested_by = message.from_user.first_name 523 | await generate_cover(requested_by, title, views, duration, thumbnail) 524 | file_path = await convert(youtube.download(url)) 525 | else: 526 | query = "" 527 | for i in message.command[1:]: 528 | query += " " + str(i) 529 | print(query) 530 | await lel.edit("🎵 **Processing**") 531 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 532 | try: 533 | results = YoutubeSearch(query, max_results=1).to_dict() 534 | url = f"https://youtube.com{results[0]['url_suffix']}" 535 | # print(results) 536 | title = results[0]["title"][:40] 537 | thumbnail = results[0]["thumbnails"][0] 538 | thumb_name = f"thumb{title}.jpg" 539 | thumb = requests.get(thumbnail, allow_redirects=True) 540 | open(thumb_name, "wb").write(thumb.content) 541 | duration = results[0]["duration"] 542 | results[0]["url_suffix"] 543 | views = results[0]["views"] 544 | 545 | except Exception as e: 546 | await lel.edit( 547 | "Song not found.Try another song or maybe spell it properly." 548 | ) 549 | print(str(e)) 550 | return 551 | 552 | dlurl = url 553 | dlurl=dlurl.replace("youtube","youtubepp") 554 | keyboard = InlineKeyboardMarkup( 555 | [ 556 | [ 557 | InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), 558 | InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), 559 | ], 560 | [ 561 | InlineKeyboardButton(text="🎬 YouTube", url=f"{url}"), 562 | InlineKeyboardButton(text="Download 📥", url=f"{dlurl}"), 563 | ], 564 | [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], 565 | ] 566 | ) 567 | requested_by = message.from_user.first_name 568 | await generate_cover(requested_by, title, views, duration, thumbnail) 569 | file_path = await convert(youtube.download(url)) 570 | chat_id = chid 571 | if chat_id in callsmusic.pytgcalls.active_calls: 572 | position = await queues.put(chat_id, file=file_path) 573 | qeue = que.get(chat_id) 574 | s_name = title 575 | r_by = message.from_user 576 | loc = file_path 577 | appendable = [s_name, r_by, loc] 578 | qeue.append(appendable) 579 | await message.reply_photo( 580 | photo="final.png", 581 | caption=f"#⃣ Your requested song **queued** at position {position}!", 582 | reply_markup=keyboard, 583 | ) 584 | os.remove("final.png") 585 | return await lel.delete() 586 | else: 587 | chat_id = chid 588 | que[chat_id] = [] 589 | qeue = que.get(chat_id) 590 | s_name = title 591 | r_by = message.from_user 592 | loc = file_path 593 | appendable = [s_name, r_by, loc] 594 | qeue.append(appendable) 595 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 596 | await message.reply_photo( 597 | photo="final.png", 598 | reply_markup=keyboard, 599 | caption="▶️ **Playing** the song requested by {} via Youtube Music 😜 in Linked Channel".format( 600 | message.from_user.mention() 601 | ), 602 | ) 603 | os.remove("final.png") 604 | return await lel.delete() 605 | 606 | 607 | @Client.on_message(filters.command(["channeldplay","cdplay"]) & filters.group & ~filters.edited) 608 | @authorized_users_only 609 | async def deezer(client: Client, message_: Message): 610 | global que 611 | lel = await message_.reply("🔄 **Processing**") 612 | 613 | try: 614 | conchat = await client.get_chat(message_.chat.id) 615 | conid = conchat.linked_chat.id 616 | conv = conchat.linked_chat 617 | chid = conid 618 | except: 619 | await message_.reply("Is chat even linked") 620 | return 621 | try: 622 | administrators = await get_administrators(conv) 623 | except: 624 | await message.reply("Am I admin of Channel") 625 | try: 626 | user = await USER.get_me() 627 | except: 628 | user.first_name = "DaisyMusic" 629 | usar = user 630 | wew = usar.id 631 | try: 632 | # chatdetails = await USER.get_chat(chid) 633 | await client.get_chat_member(chid, wew) 634 | except: 635 | for administrator in administrators: 636 | if administrator == message_.from_user.id: 637 | if message_.chat.title.startswith("Channel Music: "): 638 | await lel.edit( 639 | "Remember to add helper to your channel", 640 | ) 641 | pass 642 | try: 643 | invitelink = await client.export_chat_invite_link(chid) 644 | except: 645 | await lel.edit( 646 | "Add me as admin of yor channel first", 647 | ) 648 | return 649 | 650 | try: 651 | await USER.join_chat(invitelink) 652 | await lel.edit( 653 | "helper userbot joined your channel", 654 | ) 655 | 656 | except UserAlreadyParticipant: 657 | pass 658 | except Exception: 659 | # print(e) 660 | await lel.edit( 661 | f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your channel due to heavy requests for userbot! Make sure user is not banned in channel." 662 | "\n\nOr manually add assistant to your Group and try again", 663 | ) 664 | try: 665 | await USER.get_chat(chid) 666 | # lmoa = await client.get_chat_member(chid,wew) 667 | except: 668 | await lel.edit( 669 | f" {user.first_name} Userbot not in this channel, Ask admin to send /play command for first time or add {user.first_name} manually" 670 | ) 671 | return 672 | requested_by = message_.from_user.first_name 673 | 674 | text = message_.text.split(" ", 1) 675 | queryy = text[1] 676 | query=queryy 677 | res = lel 678 | await res.edit(f"Searching 👀👀👀 for `{queryy}` on deezer") 679 | try: 680 | songs = await arq.deezer(query,1) 681 | if not songs.ok: 682 | await message_.reply_text(songs.result) 683 | return 684 | title = songs.result[0].title 685 | url = songs.result[0].url 686 | artist = songs.result[0].artist 687 | duration = songs.result[0].duration 688 | thumbnail = songs.result[0].thumbnail 689 | except: 690 | await res.edit("Found Literally Nothing, You Should Work On Your English!") 691 | return 692 | keyboard = InlineKeyboardMarkup( 693 | [ 694 | [ 695 | InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), 696 | InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), 697 | ], 698 | [InlineKeyboardButton(text="Listen On Deezer 🎬", url=f"{url}")], 699 | [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], 700 | ] 701 | ) 702 | file_path = await convert(wget.download(url)) 703 | await res.edit("Generating Thumbnail") 704 | await generate_cover(requested_by, title, artist, duration, thumbnail) 705 | chat_id = chid 706 | if chat_id in callsmusic.pytgcalls.active_calls: 707 | await res.edit("adding in queue") 708 | position = await queues.put(chat_id, file=file_path) 709 | qeue = que.get(chat_id) 710 | s_name = title 711 | r_by = message_.from_user 712 | loc = file_path 713 | appendable = [s_name, r_by, loc] 714 | qeue.append(appendable) 715 | await res.edit_text(f"{bn}= #️⃣ Queued at position {position}") 716 | else: 717 | await res.edit_text(f"{bn}=▶️ Playing.....") 718 | 719 | que[chat_id] = [] 720 | qeue = que.get(chat_id) 721 | s_name = title 722 | r_by = message_.from_user 723 | loc = file_path 724 | appendable = [s_name, r_by, loc] 725 | qeue.append(appendable) 726 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 727 | 728 | await res.delete() 729 | 730 | m = await client.send_photo( 731 | chat_id=message_.chat.id, 732 | reply_markup=keyboard, 733 | photo="final.png", 734 | caption=f"Playing [{title}]({url}) Via Deezer in Linked Channel", 735 | ) 736 | os.remove("final.png") 737 | 738 | 739 | @Client.on_message(filters.command(["channelsplay","csplay"]) & filters.group & ~filters.edited) 740 | @authorized_users_only 741 | async def jiosaavn(client: Client, message_: Message): 742 | global que 743 | lel = await message_.reply("🔄 **Processing**") 744 | try: 745 | conchat = await client.get_chat(message_.chat.id) 746 | conid = conchat.linked_chat.id 747 | conv = conchat.linked_chat 748 | chid = conid 749 | except: 750 | await message_.reply("Is chat even linked") 751 | return 752 | try: 753 | administrators = await get_administrators(conv) 754 | except: 755 | await message.reply("Am I admin of Channel") 756 | try: 757 | user = await USER.get_me() 758 | except: 759 | user.first_name = "GeezProject" 760 | usar = user 761 | wew = usar.id 762 | try: 763 | # chatdetails = await USER.get_chat(chid) 764 | await client.get_chat_member(chid, wew) 765 | except: 766 | for administrator in administrators: 767 | if administrator == message_.from_user.id: 768 | if message_.chat.title.startswith("Channel Music: "): 769 | await lel.edit( 770 | "Remember to add helper to your channel", 771 | ) 772 | pass 773 | try: 774 | invitelink = await client.export_chat_invite_link(chid) 775 | except: 776 | await lel.edit( 777 | "Add me as admin of yor group first", 778 | ) 779 | return 780 | 781 | try: 782 | await USER.join_chat(invitelink) 783 | await lel.edit( 784 | "helper userbot joined your channel", 785 | ) 786 | 787 | except UserAlreadyParticipant: 788 | pass 789 | except Exception: 790 | # print(e) 791 | await lel.edit( 792 | f"🔴 Flood Wait Error 🔴 \nUser {user.first_name} couldn't join your channel due to heavy requests for userbot! Make sure user is not banned in group." 793 | "\n\nOr manually add @DaisyXmusic to your Group and try again", 794 | ) 795 | try: 796 | await USER.get_chat(chid) 797 | # lmoa = await client.get_chat_member(chid,wew) 798 | except: 799 | await lel.edit( 800 | " helper Userbot not in this channel, Ask channel admin to send /play command for first time or add assistant manually" 801 | ) 802 | return 803 | requested_by = message_.from_user.first_name 804 | chat_id = message_.chat.id 805 | text = message_.text.split(" ", 1) 806 | query = text[1] 807 | res = lel 808 | await res.edit(f"Searching 👀👀👀 for `{query}` on jio saavn") 809 | try: 810 | songs = await arq.saavn(query) 811 | if not songs.ok: 812 | await message_.reply_text(songs.result) 813 | return 814 | sname = songs.result[0].song 815 | slink = songs.result[0].media_url 816 | ssingers = songs.result[0].singers 817 | sthumb = "https://telegra.ph/file/f6086f8909fbfeb0844f2.png" 818 | sduration = int(songs.result[0].duration) 819 | except Exception as e: 820 | await res.edit("Found Literally Nothing!, You Should Work On Your English.") 821 | print(str(e)) 822 | return 823 | keyboard = InlineKeyboardMarkup( 824 | [ 825 | [ 826 | InlineKeyboardButton("📖 Playlist", callback_data="cplaylist"), 827 | InlineKeyboardButton("Menu ⏯ ", callback_data="cmenu"), 828 | ], 829 | [ 830 | InlineKeyboardButton( 831 | text="Join Updates Channel", url=f"https://t.me/{updateschannel}" 832 | ) 833 | ], 834 | [InlineKeyboardButton(text="❌ Close", callback_data="ccls")], 835 | ] 836 | ) 837 | file_path = await convert(wget.download(slink)) 838 | chat_id = chid 839 | if chat_id in callsmusic.pytgcalls.active_calls: 840 | position = await queues.put(chat_id, file=file_path) 841 | qeue = que.get(chat_id) 842 | s_name = sname 843 | r_by = message_.from_user 844 | loc = file_path 845 | appendable = [s_name, r_by, loc] 846 | qeue.append(appendable) 847 | await res.delete() 848 | m = await client.send_photo( 849 | chat_id=message_.chat.id, 850 | reply_markup=keyboard, 851 | photo="final.png", 852 | caption=f"{bn}=#️⃣ Queued at position {position}", 853 | ) 854 | 855 | else: 856 | await res.edit_text(f"{bn}=▶️ Playing.....") 857 | que[chat_id] = [] 858 | qeue = que.get(chat_id) 859 | s_name = sname 860 | r_by = message_.from_user 861 | loc = file_path 862 | appendable = [s_name, r_by, loc] 863 | qeue.append(appendable) 864 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 865 | await res.edit("Generating Thumbnail.") 866 | await generate_cover(requested_by, sname, ssingers, sduration, sthumb) 867 | await res.delete() 868 | m = await client.send_photo( 869 | chat_id=message_.chat.id, 870 | reply_markup=keyboard, 871 | photo="final.png", 872 | caption=f"Playing {sname} Via Jiosaavn in linked channel", 873 | ) 874 | os.remove("final.png") 875 | 876 | 877 | # Have u read all. If read RESPECT :-) 878 | -------------------------------------------------------------------------------- /GeezProject/modules/chat_member_updated.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client 2 | from pyrogram.types import ChatMemberUpdated 3 | 4 | from GeezProject.function import * 5 | 6 | 7 | @Client.on_chat_member_updated() 8 | async def chat_member_updated(_, chat_member_updated: ChatMemberUpdated): 9 | if chat_member_updated.new_chat_member \ 10 | and chat_member_updated.old_chat_member: 11 | ( 12 | admins.admins[chat_member_updated.chat.id].append( 13 | chat_member_updated.new_chat_member.user.id, 14 | ) 15 | ) if ( 16 | ( 17 | chat_member_updated.new_chat_member.can_manage_voice_chats 18 | ) and ( 19 | ( 20 | chat_member_updated.new_chat_member.user.id 21 | ) not in admins.admins[chat_member_updated.chat.id] 22 | ) 23 | ) else ( 24 | admins.admins[chat_member_updated.chat.id].remove( 25 | chat_member_updated.new_chat_member.user.id, 26 | ) 27 | ) if ( 28 | ( 29 | chat_member_updated.new_chat_member.user.id 30 | ) in admins.admins[chat_member_updated.chat.id] 31 | ) else None 32 | -------------------------------------------------------------------------------- /GeezProject/modules/gcast.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # recode by levina-lab on github 5 | # originally rewritten by using existing code (fixed) 6 | 7 | import asyncio 8 | 9 | from pyrogram import Client, filters 10 | from pyrogram.types import Dialog, Chat, Message 11 | from pyrogram.errors import UserAlreadyParticipant 12 | 13 | from GeezProject.services.callsmusic.callsmusic import client as geez 14 | from GeezProject.config import SUDO_USERS 15 | 16 | @Client.on_message(filters.command(["gcast"])) 17 | async def broadcast(_, message: Message): 18 | sent=0 19 | failed=0 20 | if message.from_user.id not in SUDO_USERS: 21 | return 22 | else: 23 | wtf = await message.reply("`memulai global cast...`") 24 | if not message.reply_to_message: 25 | await wtf.edit("balas ke pesan untuk melakukan broadcast!") 26 | return 27 | lmao = message.reply_to_message.text 28 | async for dialog in geez.iter_dialogs(): 29 | try: 30 | await geez.send_message(dialog.chat.id, lmao) 31 | sent = sent+1 32 | await wtf.edit(f"`global cast...` \n\n**mengirim ke:** `{sent}` obrolan \n**gagal di:** {failed} obrolan") 33 | await asyncio.sleep(3) 34 | except: 35 | failed=failed+1 36 | await message.reply_text(f"`gcast berhasil` \n\n**terkirim ke:** `{sent}` obrolan \n**gagal di:** {failed} obrolan") 37 | -------------------------------------------------------------------------------- /GeezProject/modules/inline.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, errors 2 | from pyrogram.types import ( 3 | InlineQuery, 4 | InlineQueryResultArticle, 5 | InputTextMessageContent, 6 | ) 7 | from youtubesearchpython import VideosSearch 8 | 9 | 10 | @Client.on_inline_query() 11 | async def inline(client: Client, query: InlineQuery): 12 | answers = [] 13 | search_query = query.query.lower().strip().rstrip() 14 | 15 | if search_query == "": 16 | await client.answer_inline_query( 17 | query.id, 18 | results=answers, 19 | switch_pm_text="Ketikkan Nama Lagu/video di YouTube...", 20 | switch_pm_parameter="help", 21 | cache_time=0, 22 | ) 23 | else: 24 | search = VideosSearch(search_query, limit=50) 25 | 26 | for result in search.result()["result"]: 27 | answers.append( 28 | InlineQueryResultArticle( 29 | title=result["title"], 30 | description="{}, {} views.".format( 31 | result["duration"], result["viewCount"]["short"] 32 | ), 33 | input_message_content=InputTextMessageContent( 34 | "https://www.youtube.com/watch?v={}".format(result["id"]) 35 | ), 36 | thumb_url=result["thumbnails"][0]["url"], 37 | ) 38 | ) 39 | 40 | try: 41 | await query.answer(results=answers, cache_time=0) 42 | except errors.QueryIdInvalid: 43 | await query.answer( 44 | results=answers, 45 | cache_time=0, 46 | switch_pm_text="Error: Search timed out", 47 | switch_pm_parameter="", 48 | ) 49 | -------------------------------------------------------------------------------- /GeezProject/modules/msg.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | import os 18 | from GeezProject.config import SOURCE_CODE,ASSISTANT_NAME,PROJECT_NAME,SUPPORT_GROUP,UPDATES_CHANNEL, OWNER 19 | class Messages(): 20 | HELP_MSG = [ 21 | ".", 22 | f""" 23 | **Hey 👋 Selamat datang kembali di {PROJECT_NAME} 24 | 25 | ✣️ {PROJECT_NAME} dapat Memutar Lagu di Voice Chat Group Dengan cara yang Mudah. 26 | 27 | ✣️ Assistant Music » @{ASSISTANT_NAME}\n\nKlik Next untuk instruksi** 28 | 29 | """, 30 | 31 | f""" 32 | **Pengaturan** 33 | 34 | 1. Jadikan bot sebagai admin 35 | 2. Mulai obrolan suara / VCG 36 | 3. Ketik `/userbotjoin` dan coba /play 37 | × Jika Assistant Bot bergabung selamat menikmati musik, 38 | × Jika Assistant Bot tidak bergabung Silahkan Tambahkan @{ASSISTANT_NAME} ke grup Anda dan coba lagi 39 | 40 | 41 | **» Perintah Untuk dalam grup Member Juga Bisa :** 42 | 43 | × /playlist : Untuk Menampilkan daftar putar Lagu sekarang 44 | × /current : Untuk Menunjukkan Lagu sekarang yang sedang diputar 45 | × /song : Untuk Mendownload lagu di YouTube 46 | × /video : Untuk Mendownload Video di YouTube dengan detail 47 | × /vsong : Untuk Mendownload Video di YouTube dengan detail 48 | × /deezer : Untuk Mendownload lagu dari deezer 49 | × /saavn : Untuk Mendownload lagu dari website saavn 50 | × /search : Untuk Mencari Video di YouTube dengan detail 51 | 52 | **» Perintah Hanya Untuk Admin :** 53 | 54 | × /play : Untuk Memutar lagu yang Anda minta melalui youtube 55 | × /play : Untuk Memutar lagu yang Anda minta melalui link youtube 56 | × /play : Untuk Memutar lagu yang Anda minta melalui file audio 57 | × /dplay : Untuk Memutar lagu yang Anda minta melalui deezer 58 | × /splay : Untuk Memutar lagu yang Anda minta melalui jio saavn 59 | × /skip : Untuk Menskip pemutaran lagu ke Lagu berikutnya 60 | × /pause : Untuk Menjeda pemutaran Lagu 61 | × /resume : Untuk Melanjutkan pemutaran Lagu yang di pause 62 | × /end : Untuk Memberhentikan pemutaran Lagu 63 | × /userbotjoin - Untuk Mengundang asisten ke obrolan Anda 64 | × /admincache - Untuk MemRefresh admin list 65 | """ 66 | ] 67 | -------------------------------------------------------------------------------- /GeezProject/modules/play.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project) 2 | # Copyright (C) 2021 Inukaasith 3 | # Copyright (C) 2021 TheHamkerCat (Python_ARQ) 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | import json 19 | import os 20 | from os import path 21 | from typing import Callable 22 | 23 | import aiofiles 24 | import aiohttp 25 | import ffmpeg 26 | import requests 27 | import wget 28 | from PIL import Image, ImageDraw, ImageFont 29 | from pyrogram import Client, filters 30 | from pyrogram.types import Voice 31 | from pyrogram.errors import UserAlreadyParticipant 32 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message 33 | from Python_ARQ import ARQ 34 | from youtube_search import YoutubeSearch 35 | 36 | from GeezProject.config import ARQ_API_KEY 37 | from GeezProject.config import BOT_NAME as bn 38 | from GeezProject.config import DURATION_LIMIT 39 | from GeezProject.config import UPDATES_CHANNEL as updateschannel 40 | from GeezProject.config import que 41 | from GeezProject.config import SOURCE_CODE,ASSISTANT_NAME,PROJECT_NAME,SUPPORT_GROUP,BOT_USERNAME, OWNER 42 | from GeezProject.function.admins import admins as a 43 | from GeezProject.helpers.admins import get_administrators 44 | from GeezProject.helpers.channelmusic import get_chat_id 45 | from GeezProject.helpers.errors import DurationLimitError 46 | from GeezProject.helpers.decorators import errors 47 | from GeezProject.helpers.decorators import authorized_users_only 48 | from GeezProject.helpers.filters import command, other_filters 49 | from GeezProject.helpers.gets import get_file_name 50 | from GeezProject.services.callsmusic import callsmusic 51 | from GeezProject.services.callsmusic.callsmusic import client as USER 52 | from GeezProject.services.converter.converter import convert 53 | from GeezProject.services.downloaders import youtube 54 | from GeezProject.services.queues import queues 55 | 56 | aiohttpsession = aiohttp.ClientSession() 57 | chat_id = None 58 | arq = ARQ("https://thearq.tech", ARQ_API_KEY, aiohttpsession) 59 | DISABLED_GROUPS = [] 60 | useer ="NaN" 61 | def cb_admin_check(func: Callable) -> Callable: 62 | async def decorator(client, cb): 63 | admemes = a.get(cb.message.chat.id) 64 | if cb.from_user.id in admemes: 65 | return await func(client, cb) 66 | else: 67 | await cb.answer("Kamu tidak diizinkan!", show_alert=True) 68 | return 69 | 70 | return decorator 71 | 72 | 73 | def transcode(filename): 74 | ffmpeg.input(filename).output( 75 | "input.raw", format="s16le", acodec="pcm_s16le", ac=2, ar="48k" 76 | ).overwrite_output().run() 77 | os.remove(filename) 78 | 79 | 80 | # Convert seconds to mm:ss 81 | def convert_seconds(seconds): 82 | seconds = seconds % (24 * 3600) 83 | seconds %= 3600 84 | minutes = seconds // 60 85 | seconds %= 60 86 | return "%02d:%02d" % (minutes, seconds) 87 | 88 | 89 | # Convert hh:mm:ss to seconds 90 | def time_to_seconds(time): 91 | stringt = str(time) 92 | return sum(int(x) * 60 ** i for i, x in enumerate(reversed(stringt.split(":")))) 93 | 94 | 95 | # Change image size 96 | def changeImageSize(maxWidth, maxHeight, image): 97 | widthRatio = maxWidth / image.size[0] 98 | heightRatio = maxHeight / image.size[1] 99 | newWidth = int(widthRatio * image.size[0]) 100 | newHeight = int(heightRatio * image.size[1]) 101 | newImage = image.resize((newWidth, newHeight)) 102 | return newImage 103 | 104 | 105 | async def generate_cover(requested_by, title, views, duration, thumbnail): 106 | async with aiohttp.ClientSession() as session: 107 | async with session.get(thumbnail) as resp: 108 | if resp.status == 200: 109 | f = await aiofiles.open("background.png", mode="wb") 110 | await f.write(await resp.read()) 111 | await f.close() 112 | 113 | image1 = Image.open("./background.png") 114 | image2 = Image.open("./etc/foreground.png") 115 | image3 = changeImageSize(1280, 720, image1) 116 | image4 = changeImageSize(1280, 720, image2) 117 | image5 = image3.convert("RGBA") 118 | image6 = image4.convert("RGBA") 119 | Image.alpha_composite(image5, image6).save("temp.png") 120 | img = Image.open("temp.png") 121 | img.save("final.png") 122 | os.remove("temp.png") 123 | os.remove("background.png") 124 | 125 | 126 | @Client.on_message(filters.command("playlist") & filters.group & ~filters.edited) 127 | async def playlist(client, message): 128 | global que 129 | if message.chat.id in DISABLED_GROUPS: 130 | return 131 | queue = que.get(message.chat.id) 132 | if not queue: 133 | await message.reply_text("**Sedang tidak Memutar lagu**") 134 | temp = [] 135 | for t in queue: 136 | temp.append(t) 137 | now_playing = temp[0][0] 138 | by = temp[0][1].mention(style="md") 139 | msg = "**Lagu Yang Sedang dimainkan** di {}".format(message.chat.title) 140 | msg += "\n• " + now_playing 141 | msg += "\n• Req by " + by 142 | temp.pop(0) 143 | if temp: 144 | msg += "\n\n" 145 | msg += "**Antrian Lagu**" 146 | for song in temp: 147 | name = song[0] 148 | usr = song[1].mention(style="md") 149 | msg += f"\n• {name}" 150 | msg += f"\n• Req by {usr}\n" 151 | await message.reply_text(msg) 152 | 153 | 154 | # ============================= Settings ========================================= 155 | 156 | 157 | def updated_stats(chat, queue, vol=100): 158 | if chat.id in callsmusic.pytgcalls.active_calls: 159 | # if chat.id in active_chats: 160 | stats = "Pengaturan dari **{}**".format(chat.title) 161 | if len(que) > 0: 162 | stats += "\n\n" 163 | stats += "Volume : {}%\n".format(vol) 164 | stats += "Lagu dalam antrian : `{}`\n".format(len(que)) 165 | stats += "Sedang memutar lagu : **{}**\n".format(queue[0][0]) 166 | stats += "Requested by : {}".format(queue[0][1].mention) 167 | else: 168 | stats = None 169 | return stats 170 | 171 | 172 | def r_ply(type_): 173 | if type_ == "play": 174 | pass 175 | else: 176 | pass 177 | mar = InlineKeyboardMarkup( 178 | [ 179 | [ 180 | InlineKeyboardButton("⏹", "leave"), 181 | InlineKeyboardButton("⏸", "puse"), 182 | InlineKeyboardButton("▶️", "resume"), 183 | InlineKeyboardButton("⏭", "skip"), 184 | ], 185 | [ 186 | InlineKeyboardButton("📖 Playlist", "playlist"), 187 | ], 188 | [InlineKeyboardButton("🗑 Close", "cls")], 189 | ] 190 | ) 191 | return mar 192 | 193 | 194 | @Client.on_message(filters.command("current") & filters.group & ~filters.edited) 195 | async def ee(client, message): 196 | if message.chat.id in DISABLED_GROUPS: 197 | return 198 | queue = que.get(message.chat.id) 199 | stats = updated_stats(message.chat, queue) 200 | if stats: 201 | await message.reply(stats) 202 | else: 203 | await message.reply("**Silahkan Nyalakan dulu VCG nya!**") 204 | 205 | 206 | @Client.on_message(filters.command("player") & filters.group & ~filters.edited) 207 | @authorized_users_only 208 | async def settings(client, message): 209 | if message.chat.id in DISABLED_GROUPS: 210 | await message.reply("**Music Player dimatikan**") 211 | return 212 | playing = None 213 | chat_id = get_chat_id(message.chat) 214 | if chat_id in callsmusic.pytgcalls.active_calls: 215 | playing = True 216 | queue = que.get(chat_id) 217 | stats = updated_stats(message.chat, queue) 218 | if stats: 219 | if playing: 220 | await message.reply(stats, reply_markup=r_ply("pause")) 221 | 222 | else: 223 | await message.reply(stats, reply_markup=r_ply("play")) 224 | else: 225 | await message.reply("**Silahkan Nyalakan dulu VCG nya!**") 226 | 227 | 228 | @Client.on_message( 229 | filters.command("musicplayer") & ~filters.edited & ~filters.bot & ~filters.private 230 | ) 231 | @authorized_users_only 232 | async def hfmm(_, message): 233 | global DISABLED_GROUPS 234 | try: 235 | user_id = message.from_user.id 236 | except: 237 | return 238 | if len(message.command) != 2: 239 | await message.reply_text( 240 | "**Saya hanya mengenali** `/musicplayer on` **dan** `/musicplayer off`" 241 | ) 242 | return 243 | status = message.text.split(None, 1)[1] 244 | message.chat.id 245 | if status == "ON" or status == "on" or status == "On": 246 | lel = await message.reply("`Processing...`") 247 | if not message.chat.id in DISABLED_GROUPS: 248 | await lel.edit("**Pemutar Musik Sudah Diaktifkan Di Obrolan Ini**") 249 | return 250 | DISABLED_GROUPS.remove(message.chat.id) 251 | await lel.edit( 252 | f"**Pemutar Musik Berhasil Diaktifkan Untuk Pengguna Dalam Obrolan** {message.chat.id}" 253 | ) 254 | 255 | elif status == "OFF" or status == "off" or status == "Off": 256 | lel = await message.reply("`Processing...`") 257 | 258 | if message.chat.id in DISABLED_GROUPS: 259 | await lel.edit("**Pemutar Musik Sudah dimatikan Dalam Obrolan Ini**") 260 | return 261 | DISABLED_GROUPS.append(message.chat.id) 262 | await lel.edit( 263 | f"**Pemutar Musik Berhasil Dinonaktifkan Untuk Pengguna Dalam Obrolan** {message.chat.id}" 264 | ) 265 | else: 266 | await message.reply_text( 267 | "**Saya hanya mengenali** `/musicplayer on` **dan** `/musicplayer off`" 268 | ) 269 | 270 | 271 | @Client.on_callback_query(filters.regex(pattern=r"^(playlist)$")) 272 | async def p_cb(b, cb): 273 | global que 274 | que.get(cb.message.chat.id) 275 | type_ = cb.matches[0].group(1) 276 | cb.message.chat.id 277 | cb.message.chat 278 | cb.message.reply_markup.inline_keyboard[1][0].callback_data 279 | if type_ == "playlist": 280 | queue = que.get(cb.message.chat.id) 281 | if not queue: 282 | await cb.message.edit("**Sedang tidak Memutar lagu**") 283 | temp = [] 284 | for t in queue: 285 | temp.append(t) 286 | now_playing = temp[0][0] 287 | by = temp[0][1].mention(style="md") 288 | msg = "**Lagu Yang Sedang dimainkan** di {}".format(cb.message.chat.title) 289 | msg += "\n• " + now_playing 290 | msg += "\n• Req by " + by 291 | temp.pop(0) 292 | if temp: 293 | msg += "\n\n" 294 | msg += "**Antrian Lagu**" 295 | for song in temp: 296 | name = song[0] 297 | usr = song[1].mention(style="md") 298 | msg += f"\n• {name}" 299 | msg += f"\n• Req by {usr}\n" 300 | await cb.message.edit(msg) 301 | 302 | 303 | @Client.on_callback_query( 304 | filters.regex(pattern=r"^(play|pause|skip|leave|puse|resume|menu|cls)$") 305 | ) 306 | @cb_admin_check 307 | async def m_cb(b, cb): 308 | global que 309 | if ( 310 | cb.message.chat.title.startswith("Channel Music: ") 311 | and chat.title[14:].isnumeric() 312 | ): 313 | chet_id = int(chat.title[13:]) 314 | else: 315 | chet_id = cb.message.chat.id 316 | qeue = que.get(chet_id) 317 | type_ = cb.matches[0].group(1) 318 | cb.message.chat.id 319 | m_chat = cb.message.chat 320 | 321 | the_data = cb.message.reply_markup.inline_keyboard[1][0].callback_data 322 | if type_ == "pause": 323 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 324 | callsmusic.pytgcalls.active_calls[chet_id] == "paused" 325 | ): 326 | await cb.answer("Chat is not connected!", show_alert=True) 327 | else: 328 | callsmusic.pytgcalls.pause_stream(chet_id) 329 | 330 | await cb.answer("Music Paused!") 331 | await cb.message.edit( 332 | updated_stats(m_chat, qeue), reply_markup=r_ply("play") 333 | ) 334 | 335 | elif type_ == "play": 336 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 337 | callsmusic.pytgcalls.active_calls[chet_id] == "playing" 338 | ): 339 | await cb.answer("Chat is not connected!", show_alert=True) 340 | else: 341 | callsmusic.pytgcalls.resume_stream(chet_id) 342 | await cb.answer("Music Resumed!") 343 | await cb.message.edit( 344 | updated_stats(m_chat, qeue), reply_markup=r_ply("pause") 345 | ) 346 | 347 | elif type_ == "playlist": 348 | queue = que.get(cb.message.chat.id) 349 | if not queue: 350 | await cb.message.edit("Player is idle") 351 | temp = [] 352 | for t in queue: 353 | temp.append(t) 354 | now_playing = temp[0][0] 355 | by = temp[0][1].mention(style="md") 356 | msg = "**Lagu Yang Sedang dimainkan** di {}".format(cb.message.chat.title) 357 | msg += "\n• " + now_playing 358 | msg += "\n• Req by " + by 359 | temp.pop(0) 360 | if temp: 361 | msg += "\n\n" 362 | msg += "**Antrian Lagu**" 363 | for song in temp: 364 | name = song[0] 365 | usr = song[1].mention(style="md") 366 | msg += f"\n• {name}" 367 | msg += f"\n• Req by {usr}\n" 368 | await cb.message.edit(msg) 369 | 370 | elif type_ == "resume": 371 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 372 | callsmusic.pytgcalls.active_calls[chet_id] == "playing" 373 | ): 374 | await cb.answer("Chat is not connected or already playng", show_alert=True) 375 | else: 376 | callsmusic.pytgcalls.resume_stream(chet_id) 377 | await cb.answer("Music Resumed!") 378 | elif type_ == "puse": 379 | if (chet_id not in callsmusic.pytgcalls.active_calls) or ( 380 | callsmusic.pytgcalls.active_calls[chet_id] == "paused" 381 | ): 382 | await cb.answer("Chat is not connected or already paused", show_alert=True) 383 | else: 384 | callsmusic.pytgcalls.pause_stream(chet_id) 385 | 386 | await cb.answer("Music Paused!") 387 | elif type_ == "cls": 388 | await cb.answer("Closed menu") 389 | await cb.message.delete() 390 | 391 | elif type_ == "menu": 392 | stats = updated_stats(cb.message.chat, qeue) 393 | await cb.answer("Menu opened") 394 | marr = InlineKeyboardMarkup( 395 | [ 396 | [ 397 | InlineKeyboardButton("⏹", "leave"), 398 | InlineKeyboardButton("⏸", "puse"), 399 | InlineKeyboardButton("▶️", "resume"), 400 | InlineKeyboardButton("⏭", "skip"), 401 | ], 402 | [ 403 | InlineKeyboardButton("📖 Playlist", "playlist"), 404 | ], 405 | [InlineKeyboardButton("❌ Close", "cls")], 406 | ] 407 | ) 408 | await cb.message.edit(stats, reply_markup=marr) 409 | elif type_ == "skip": 410 | if qeue: 411 | qeue.pop(0) 412 | if chet_id not in callsmusic.pytgcalls.active_calls: 413 | await cb.answer("Chat is not connected!", show_alert=True) 414 | else: 415 | callsmusic.queues.task_done(chet_id) 416 | 417 | if callsmusic.queues.is_empty(chet_id): 418 | callsmusic.pytgcalls.leave_group_call(chet_id) 419 | 420 | await cb.message.edit("- No More Playlist..\n- Leaving VC!") 421 | else: 422 | callsmusic.pytgcalls.change_stream( 423 | chet_id, callsmusic.queues.get(chet_id)["file"] 424 | ) 425 | await cb.answer("Skipped") 426 | await cb.message.edit((m_chat, qeue), reply_markup=r_ply(the_data)) 427 | await cb.message.reply_text( 428 | f"- Skipped track\n- Now Playing **{qeue[0][0]}**" 429 | ) 430 | 431 | else: 432 | if chet_id in callsmusic.pytgcalls.active_calls: 433 | try: 434 | callsmusic.queues.clear(chet_id) 435 | except QueueEmpty: 436 | pass 437 | 438 | callsmusic.pytgcalls.leave_group_call(chet_id) 439 | await cb.message.edit("Successfully Left the Chat!") 440 | else: 441 | await cb.answer("Chat is not connected!", show_alert=True) 442 | 443 | 444 | @Client.on_message(command("play") & other_filters) 445 | async def play(_, message: Message): 446 | global que 447 | global useer 448 | if message.chat.id in DISABLED_GROUPS: 449 | return 450 | lel = await message.reply("🔄 **Sedang Memproses Lagu**") 451 | administrators = await get_administrators(message.chat) 452 | chid = message.chat.id 453 | 454 | try: 455 | user = await USER.get_me() 456 | except: 457 | user.first_name = "GeezProject" 458 | usar = user 459 | wew = usar.id 460 | try: 461 | # chatdetails = await USER.get_chat(chid) 462 | await _.get_chat_member(chid, wew) 463 | except: 464 | for administrator in administrators: 465 | if administrator == message.from_user.id: 466 | if message.chat.title.startswith("Channel Music: "): 467 | await lel.edit( 468 | f"Ingatlah untuk menambahkan {user.first_name} ke Channel Anda", 469 | ) 470 | pass 471 | try: 472 | invitelink = await _.export_chat_invite_link(chid) 473 | except: 474 | await lel.edit( 475 | "Tambahkan saya sebagai admin grup Anda terlebih dahulu", 476 | ) 477 | return 478 | 479 | try: 480 | await USER.join_chat(invitelink) 481 | await USER.send_message( 482 | message.chat.id, "I joined this group for playing music in VC" 483 | ) 484 | await lel.edit( 485 | "helper userbot joined your chat", 486 | ) 487 | 488 | except UserAlreadyParticipant: 489 | pass 490 | except Exception: 491 | # print(e) 492 | await lel.edit( 493 | f"⛑ Flood Wait Error ⛑\n{user.first_name} tidak dapat bergabung dengan grup Anda karena banyaknya permintaan bergabung untuk userbot! Pastikan pengguna tidak dibanned dalam grup." 494 | f"\n\nAtau tambahkan @{ASSISTANT_NAME} secara manual ke Grup Anda dan coba lagi", 495 | ) 496 | try: 497 | await USER.get_chat(chid) 498 | # lmoa = await client.get_chat_member(chid,wew) 499 | except: 500 | await lel.edit( 501 | f"{user.first_name} terkena banned dari Grup ini, Minta admin untuk mengirim perintah `/play` untuk pertama kalinya atau tambahkan @{ASSISTANT_NAME} secara manual" 502 | ) 503 | return 504 | text_links=None 505 | await lel.edit("🔄 **Sedang Mencari Lagu**") 506 | if message.reply_to_message: 507 | entities = [] 508 | toxt = message.reply_to_message.text or message.reply_to_message.caption 509 | if message.reply_to_message.entities: 510 | entities = message.reply_to_message.entities + entities 511 | elif message.reply_to_message.caption_entities: 512 | entities = message.reply_to_message.entities + entities 513 | urls = [entity for entity in entities if entity.type == 'url'] 514 | text_links = [ 515 | entity for entity in entities if entity.type == 'text_link' 516 | ] 517 | else: 518 | urls=None 519 | if text_links: 520 | urls = True 521 | user_id = message.from_user.id 522 | user_name = message.from_user.first_name 523 | rpk = "[" + user_name + "](tg://user?id=" + str(user_id) + ")" 524 | audio = ( 525 | (message.reply_to_message.audio or message.reply_to_message.voice) 526 | if message.reply_to_message 527 | else None 528 | ) 529 | if audio: 530 | if round(audio.duration / 60) > DURATION_LIMIT: 531 | raise DurationLimitError( 532 | f"❌ **Lagu dengan durasi lebih dari** `{DURATION_LIMIT}` **menit tidak boleh diputar!**" 533 | ) 534 | keyboard = InlineKeyboardMarkup( 535 | [ 536 | [ 537 | InlineKeyboardButton("📌 Groups", url="https://t.me/GeezSupportGroup"), 538 | InlineKeyboardButton("⛑ Channel", url="https://t.me/GeezProjets"), 539 | ], 540 | [InlineKeyboardButton(text="🗑 Close", callback_data="cls")], 541 | ] 542 | ) 543 | file_name = get_file_name(audio) 544 | title = file_name 545 | thumb_name = "https://telegra.ph/file/fa2cdb8a14a26950da711.png" 546 | thumbnail = thumb_name 547 | duration = round(audio.duration / 60) 548 | views = "Locally added" 549 | requested_by = message.from_user.first_name 550 | await generate_cover(requested_by, title, views, duration, thumbnail) 551 | file_path = await convert( 552 | (await message.reply_to_message.download(file_name)) 553 | if not path.isfile(path.join("downloads", file_name)) 554 | else file_name 555 | ) 556 | elif urls: 557 | query = toxt 558 | await lel.edit("🎵 **Sedang Memproses Lagu**") 559 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 560 | try: 561 | results = YoutubeSearch(query, max_results=1).to_dict() 562 | url = f"https://youtube.com{results[0]['url_suffix']}" 563 | # print(results) 564 | title = results[0]["title"][:25] 565 | thumbnail = results[0]["thumbnails"][0] 566 | thumb_name = f"thumb{title}.jpg" 567 | thumb = requests.get(thumbnail, allow_redirects=True) 568 | open(thumb_name, "wb").write(thumb.content) 569 | duration = results[0]["duration"] 570 | results[0]["url_suffix"] 571 | views = results[0]["views"] 572 | 573 | except Exception as e: 574 | await lel.edit( 575 | "**Lagu tidak ditemukan.** Coba cari dengan judul lagu yang lebih jelas, Ketik `/help` bila butuh bantuan" 576 | ) 577 | print(str(e)) 578 | return 579 | dlurl=url 580 | dlurl=dlurl.replace("youtube","youtubepp") 581 | keyboard = InlineKeyboardMarkup( 582 | [ 583 | [ 584 | InlineKeyboardButton("📌 Groups", url="https://t.me/GeezSupportGroup"), 585 | InlineKeyboardButton("⛑ Channel", url="https://t.me/GeezProjects"), 586 | ], 587 | [InlineKeyboardButton(text="🗑 Close", callback_data="cls")], 588 | ] 589 | ) 590 | requested_by = message.from_user.first_name 591 | await generate_cover(requested_by, title, views, duration, thumbnail) 592 | file_path = await convert(youtube.download(url)) 593 | else: 594 | query = "" 595 | for i in message.command[1:]: 596 | query += " " + str(i) 597 | print(query) 598 | await lel.edit("🎵 **Sedang Memproses Lagu**") 599 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 600 | 601 | try: 602 | results = YoutubeSearch(query, max_results=5).to_dict() 603 | except: 604 | await lel.edit("**berikan judul lagu yang ingin kamu putar !**") 605 | # Looks like hell. Aren't it?? FUCK OFF 606 | try: 607 | toxxt = "**__silahkan pilih lagu untuk diputar:__**\n\n" 608 | j = 0 609 | useer=user_name 610 | emojilist = ["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣",] 611 | 612 | while j < 5: 613 | toxxt += f"{emojilist[j]} [{results[j]['title'][:25]}](https://youtube.com{results[j]['url_suffix']})\n" 614 | toxxt += f" ├ 💡 **Duration** - {results[j]['duration']}\n" 615 | toxxt += f" └ ⚡ __Powered by Geez Music Project__\n\n" 616 | 617 | j += 1 618 | koyboard = InlineKeyboardMarkup( 619 | [ 620 | [ 621 | InlineKeyboardButton("1️⃣", callback_data=f'plll 0|{query}|{user_id}'), 622 | InlineKeyboardButton("2️⃣", callback_data=f'plll 1|{query}|{user_id}'), 623 | InlineKeyboardButton("3️⃣", callback_data=f'plll 2|{query}|{user_id}'), 624 | ], 625 | [ 626 | InlineKeyboardButton("4️⃣", callback_data=f'plll 3|{query}|{user_id}'), 627 | InlineKeyboardButton("5️⃣", callback_data=f'plll 4|{query}|{user_id}'), 628 | ], 629 | [InlineKeyboardButton(text="🗑 Close", callback_data="cls")], 630 | ] 631 | ) 632 | await lel.edit(toxxt,reply_markup=koyboard,disable_web_page_preview=True) 633 | # WHY PEOPLE ALWAYS LOVE PORN ?? (A point to think) 634 | return 635 | # Returning to pornhub 636 | except: 637 | await lel.edit("**Tidak ada hasil yang cukup untuk dipilih.. Mulai bermain langsung..**") 638 | 639 | # print(results) 640 | try: 641 | url = f"https://youtube.com{results[0]['url_suffix']}" 642 | title = results[0]["title"][:25] 643 | thumbnail = results[0]["thumbnails"][0] 644 | thumb_name = f"thumb{title}.jpg" 645 | thumb = requests.get(thumbnail, allow_redirects=True) 646 | open(thumb_name, "wb").write(thumb.content) 647 | duration = results[0]["duration"] 648 | results[0]["url_suffix"] 649 | views = results[0]["views"] 650 | 651 | except Exception as e: 652 | await lel.edit( 653 | "**Lagu tidak ditemukan.** Coba cari dengan judul lagu yang lebih jelas, Ketik `/help` bila butuh bantuan" 654 | ) 655 | print(str(e)) 656 | return 657 | dlurl=url 658 | dlurl=dlurl.replace("youtube","youtubepp") 659 | keyboard = InlineKeyboardMarkup( 660 | [ 661 | [ 662 | InlineKeyboardButton("📌 Groups", url="https://t.me/GeezSupportGroup"), 663 | InlineKeyboardButton("⛑ Channel", url="https://t.me/GeezProjects"), 664 | ], 665 | [InlineKeyboardButton(text="🗑 Close", callback_data="cls")], 666 | ] 667 | ) 668 | requested_by = message.from_user.first_name 669 | await generate_cover(requested_by, title, views, duration, thumbnail) 670 | file_path = await convert(youtube.download(url)) 671 | chat_id = get_chat_id(message.chat) 672 | if chat_id in callsmusic.pytgcalls.active_calls: 673 | position = await queues.put(chat_id, file=file_path) 674 | qeue = que.get(chat_id) 675 | s_name = title 676 | r_by = message.from_user 677 | loc = file_path 678 | appendable = [s_name, r_by, loc] 679 | qeue.append(appendable) 680 | await message.reply_photo( 681 | photo="final.png", 682 | caption = f"🏷 **Judul:** [{title[:30]}]({url})\n⏱ **Durasi:** {duration}\n💡 **Status:** Antrian Ke `{position}`\n" \ 683 | + f"🎧 **Request Dari:** {message.from_user.mention}", 684 | reply_markup=keyboard) 685 | 686 | else: 687 | chat_id = get_chat_id(message.chat) 688 | que[chat_id] = [] 689 | qeue = que.get(chat_id) 690 | s_name = title 691 | r_by = message.from_user 692 | loc = file_path 693 | appendable = [s_name, r_by, loc] 694 | qeue.append(appendable) 695 | try: 696 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 697 | except: 698 | message.reply("**Voice Chat Group tidak aktif, Saya tidak dapat bergabung**") 699 | return 700 | await message.reply_photo( 701 | photo="final.png", 702 | caption = f"🏷 **Judul:** [{title[:30]}]({url})\n⏱ **Durasi:** {duration}\n💡 **Status:** Sedang Memutar\n" \ 703 | + f"🎧 **Request Dari:** {message.from_user.mention}", 704 | reply_markup=keyboard) 705 | 706 | os.remove("final.png") 707 | return await lel.delete() 708 | 709 | 710 | @Client.on_message(filters.command("ytplay") & filters.group & ~filters.edited) 711 | async def ytplay(_, message: Message): 712 | global que 713 | if message.chat.id in DISABLED_GROUPS: 714 | return 715 | lel = await message.reply("🔄 **Sedang Memproses Lagu**") 716 | administrators = await get_administrators(message.chat) 717 | chid = message.chat.id 718 | 719 | try: 720 | user = await USER.get_me() 721 | except: 722 | user.first_name = "GeezProject" 723 | usar = user 724 | wew = usar.id 725 | try: 726 | # chatdetails = await USER.get_chat(chid) 727 | await _.get_chat_member(chid, wew) 728 | except: 729 | for administrator in administrators: 730 | if administrator == message.from_user.id: 731 | if message.chat.title.startswith("Channel Music: "): 732 | await lel.edit( 733 | f"Ingatlah untuk menambahkan {user.first_name} ke Channel Anda", 734 | ) 735 | pass 736 | try: 737 | invitelink = await _.export_chat_invite_link(chid) 738 | except: 739 | await lel.edit( 740 | "Tambahkan saya sebagai admin grup Anda terlebih dahulu", 741 | ) 742 | return 743 | 744 | try: 745 | await USER.join_chat(invitelink) 746 | await USER.send_message( 747 | message.chat.id, "I joined this group for playing music in VC" 748 | ) 749 | await lel.edit( 750 | "helper userbot joined your chat", 751 | ) 752 | 753 | except UserAlreadyParticipant: 754 | pass 755 | except Exception: 756 | # print(e) 757 | await lel.edit( 758 | f"Flood Wait Error\n{user.first_name} tidak dapat bergabung dengan grup Anda karena banyaknya permintaan bergabung untuk userbot! Pastikan pengguna tidak dibanned dalam grup." 759 | f"\n\nAtau tambahkan @{ASSISTANT_NAME} secara manual ke Grup Anda dan coba lagi", 760 | ) 761 | try: 762 | await USER.get_chat(chid) 763 | # lmoa = await client.get_chat_member(chid,wew) 764 | except: 765 | await lel.edit( 766 | f"{user.first_name} terkena banned dari Grup ini, Minta admin untuk mengirim perintah `/play` untuk pertama kalinya atau tambahkan @{ASSISTANT_NAME} secara manual" 767 | ) 768 | return 769 | await lel.edit("🔎 **Sedang Mencari Lagu**") 770 | user_id = message.from_user.id 771 | user_name = message.from_user.first_name 772 | 773 | 774 | query = "" 775 | for i in message.command[1:]: 776 | query += " " + str(i) 777 | print(query) 778 | await lel.edit("🎵 **Sedang Memproses Lagu**") 779 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 780 | try: 781 | results = YoutubeSearch(query, max_results=1).to_dict() 782 | url = f"https://youtube.com{results[0]['url_suffix']}" 783 | # print(results) 784 | title = results[0]["title"][:25] 785 | thumbnail = results[0]["thumbnails"][0] 786 | thumb_name = f"thumb{title}.jpg" 787 | thumb = requests.get(thumbnail, allow_redirects=True) 788 | open(thumb_name, "wb").write(thumb.content) 789 | duration = results[0]["duration"] 790 | results[0]["url_suffix"] 791 | views = results[0]["views"] 792 | 793 | except Exception as e: 794 | await lel.edit( 795 | "**Lagu tidak ditemukan.** Coba cari dengan judul lagu yang lebih jelas, Ketik `/help` bila butuh bantuan" 796 | ) 797 | print(str(e)) 798 | return 799 | dlurl=url 800 | dlurl=dlurl.replace("youtube","youtubepp") 801 | keyboard = InlineKeyboardMarkup( 802 | [ 803 | [ 804 | InlineKeyboardButton("📌 Groups", url="https://t.me/GeezSupportGroup"), 805 | InlineKeyboardButton("⛑ Channel", url="https://t.me/GeezProjects"), 806 | ], 807 | [InlineKeyboardButton(text="🗑 Close", callback_data="cls")], 808 | ] 809 | ) 810 | requested_by = message.from_user.first_name 811 | await generate_cover(requested_by, title, views, duration, thumbnail) 812 | file_path = await convert(youtube.download(url)) 813 | chat_id = get_chat_id(message.chat) 814 | if chat_id in callsmusic.pytgcalls.active_calls: 815 | position = await queues.put(chat_id, file=file_path) 816 | qeue = que.get(chat_id) 817 | s_name = title 818 | r_by = message.from_user 819 | loc = file_path 820 | appendable = [s_name, r_by, loc] 821 | qeue.append(appendable) 822 | await message.reply_photo( 823 | photo="final.png", 824 | caption = f"🏷 **Judul:** [{title[:30]}]({url})\n⏱ **Durasi:** {duration}\n💡 **Status:** Antrian Ke `{position}`\n" \ 825 | + f"🎧 **Request Dari:** {message.from_user.mention}", 826 | reply_markup=keyboard, 827 | ) 828 | os.remove("final.png") 829 | return await lel.delete() 830 | else: 831 | chat_id = get_chat_id(message.chat) 832 | que[chat_id] = [] 833 | qeue = que.get(chat_id) 834 | s_name = title 835 | r_by = message.from_user 836 | loc = file_path 837 | appendable = [s_name, r_by, loc] 838 | qeue.append(appendable) 839 | try: 840 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 841 | except: 842 | message.reply("**Voice Chat Group tidak aktif, Saya tidak dapat bergabung**") 843 | return 844 | await message.reply_photo( 845 | photo="final.png", 846 | caption = f"🏷 **Judul:** [{title[:30]}]({url})\n⏱ **Durasi:** {duration}\n💡 **Status:** Sedang Memutar\n" \ 847 | + f"🎧 **Request Dari:** {message.from_user.mention}", 848 | reply_markup=keyboard,) 849 | os.remove("final.png") 850 | return await lel.delete() 851 | 852 | 853 | @Client.on_message(filters.command("dplay") & filters.group & ~filters.edited) 854 | async def deezer(client: Client, message_: Message): 855 | if message_.chat.id in DISABLED_GROUPS: 856 | return 857 | global que 858 | lel = await message_.reply("🔄 **Sedang Memproses Lagu**") 859 | administrators = await get_administrators(message_.chat) 860 | chid = message_.chat.id 861 | try: 862 | user = await USER.get_me() 863 | except: 864 | user.first_name = "GeezProject" 865 | usar = user 866 | wew = usar.id 867 | try: 868 | # chatdetails = await USER.get_chat(chid) 869 | await client.get_chat_member(chid, wew) 870 | except: 871 | for administrator in administrators: 872 | if administrator == message_.from_user.id: 873 | if message_.chat.title.startswith("Channel Music: "): 874 | await lel.edit( 875 | f"Ingatlah untuk menambahkan {user.first_name} ke Channel Anda", 876 | ) 877 | pass 878 | try: 879 | invitelink = await client.export_chat_invite_link(chid) 880 | except: 881 | await lel.edit( 882 | "Tambahkan saya sebagai admin grup Anda terlebih dahulu", 883 | ) 884 | return 885 | 886 | try: 887 | await USER.join_chat(invitelink) 888 | await USER.send_message( 889 | message_.chat.id, "I joined this group for playing music in VC" 890 | ) 891 | await lel.edit( 892 | "helper userbot joined your chat", 893 | ) 894 | 895 | except UserAlreadyParticipant: 896 | pass 897 | except Exception: 898 | # print(e) 899 | await lel.edit( 900 | f"⛑ Flood Wait Error ⛑\n{user.first_name} tidak dapat bergabung dengan grup Anda karena banyaknya permintaan bergabung untuk userbot! Pastikan pengguna tidak dibanned dalam grup." 901 | f"\n\nAtau tambahkan @{ASSISTANT_NAME} secara manual ke Grup Anda dan coba lagi", 902 | ) 903 | try: 904 | await USER.get_chat(chid) 905 | # lmoa = await client.get_chat_member(chid,wew) 906 | except: 907 | await lel.edit( 908 | f"{user.first_name} terkena banned dari Grup ini, Minta admin untuk mengirim perintah `/play` untuk pertama kalinya atau tambahkan @{ASSISTANT_NAME} secara manual" 909 | ) 910 | return 911 | requested_by = message_.from_user.first_name 912 | 913 | text = message_.text.split(" ", 1) 914 | queryy = text[1] 915 | query = queryy 916 | res = lel 917 | await res.edit(f"**Sedang Mencari Lagu** `{query}` **dari deezer**") 918 | try: 919 | songs = await arq.deezer(query,1) 920 | if not songs.ok: 921 | await message_.reply_text(songs.result) 922 | return 923 | title = songs.result[0].title 924 | url = songs.result[0].url 925 | artist = songs.result[0].artist 926 | duration = songs.result[0].duration 927 | thumbnail = "https://telegra.ph/file/fa2cdb8a14a26950da711.png" 928 | 929 | except: 930 | await res.edit("**Tidak Ditemukan Lagu Apa Pun!**") 931 | return 932 | try: 933 | duuration= round(duration / 60) 934 | if duuration > DURATION_LIMIT: 935 | await cb.message.edit(f"**Musik lebih lama dari** `{DURATION_LIMIT}` **menit tidak diperbolehkan diputar**") 936 | return 937 | except: 938 | pass 939 | 940 | keyboard = InlineKeyboardMarkup( 941 | [ 942 | [InlineKeyboardButton(text="⛑ Channel", url="https://t.me/GeezProjects")], 943 | ] 944 | ) 945 | file_path = await convert(wget.download(url)) 946 | await res.edit("📥 **Generating Thumbnail**") 947 | await generate_cover(requested_by, title, artist, duration, thumbnail) 948 | chat_id = get_chat_id(message_.chat) 949 | if chat_id in callsmusic.pytgcalls.active_calls: 950 | await res.edit("adding in queue") 951 | position = await queues.put(chat_id, file=file_path) 952 | qeue = que.get(chat_id) 953 | s_name = title 954 | r_by = message_.from_user 955 | loc = file_path 956 | appendable = [s_name, r_by, loc] 957 | qeue.append(appendable) 958 | await res.edit_text(f"🎼 **Lagu yang Anda minta Sedang Antri di posisi** `{position}`") 959 | else: 960 | await res.edit_text(f"🎼️ **Playing...**") 961 | 962 | que[chat_id] = [] 963 | qeue = que.get(chat_id) 964 | s_name = title 965 | r_by = message_.from_user 966 | loc = file_path 967 | appendable = [s_name, r_by, loc] 968 | qeue.append(appendable) 969 | try: 970 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 971 | except: 972 | res.edit("Voice Chat Group tidak aktif, Saya tidak dapat bergabung") 973 | return 974 | 975 | await res.delete() 976 | 977 | m = await client.send_photo( 978 | chat_id=message_.chat.id, 979 | reply_markup=keyboard, 980 | photo="final.png", 981 | caption=f"🎼️ **Sedang Memutar Lagu** [{title}]({url}) **Via Deezer**", 982 | ) 983 | os.remove("final.png") 984 | 985 | 986 | @Client.on_callback_query(filters.regex(pattern=r"plll")) 987 | async def lol_cb(b, cb): 988 | global que 989 | 990 | cbd = cb.data.strip() 991 | chat_id = cb.message.chat.id 992 | typed_=cbd.split(None, 1)[1] 993 | #useer_id = cb.message.reply_to_message.from_user.id 994 | try: 995 | x,query,useer_id = typed_.split("|") 996 | except: 997 | await cb.message.edit("Lagu Tidak ditemukan") 998 | return 999 | useer_id = int(useer_id) 1000 | if cb.from_user.id != useer_id: 1001 | await cb.answer("Anda bukan orang yang meminta untuk memutar lagu!", show_alert=True) 1002 | return 1003 | await cb.message.edit("**Processing**") 1004 | x=int(x) 1005 | try: 1006 | useer_name = cb.message.reply_to_message.from_user.first_name 1007 | except: 1008 | useer_name = cb.message.from_user.first_name 1009 | 1010 | results = YoutubeSearch(query, max_results=5).to_dict() 1011 | resultss=results[x]["url_suffix"] 1012 | title=results[x]["title"][:25] 1013 | thumbnail=results[x]["thumbnails"][0] 1014 | duration=results[x]["duration"] 1015 | views=results[x]["views"] 1016 | url = f"https://youtube.com{resultss}" 1017 | 1018 | try: 1019 | duuration= round(duration / 60) 1020 | if duuration > DURATION_LIMIT: 1021 | await cb.message.edit(f"Lagu lebih lama dari {DURATION_LIMIT} menit tidak diperbolehkan diputar") 1022 | return 1023 | except: 1024 | pass 1025 | try: 1026 | thumb_name = f"thumb{title}.jpg" 1027 | thumb = requests.get(thumbnail, allow_redirects=True) 1028 | open(thumb_name, "wb").write(thumb.content) 1029 | except Exception as e: 1030 | print(e) 1031 | return 1032 | dlurl=url 1033 | dlurl=dlurl.replace("youtube","youtubepp") 1034 | keyboard = InlineKeyboardMarkup( 1035 | [ 1036 | [ 1037 | InlineKeyboardButton("📌 Groups", url="https://t.me/GeezSupportGroup"), 1038 | InlineKeyboardButton("🛡️ Channel", url="https://t.me/GeezProjects"), 1039 | ], 1040 | [InlineKeyboardButton(text="🗑 Close", callback_data="cls")], 1041 | ] 1042 | ) 1043 | requested_by = useer_name 1044 | await generate_cover(requested_by, title, views, duration, thumbnail) 1045 | file_path = await convert(youtube.download(url)) 1046 | if chat_id in callsmusic.pytgcalls.active_calls: 1047 | position = await queues.put(chat_id, file=file_path) 1048 | qeue = que.get(chat_id) 1049 | s_name = title 1050 | try: 1051 | r_by = cb.message.reply_to_message.from_user 1052 | except: 1053 | r_by = cb.message.from_user 1054 | loc = file_path 1055 | appendable = [s_name, r_by, loc] 1056 | qeue.append(appendable) 1057 | await cb.message.delete() 1058 | await b.send_photo(chat_id, 1059 | photo="final.png", 1060 | caption = f"🏷 **Judul:** [{title[:30]}]({url})\n⏱ **Durasi:** {duration}\n💡 **Status:** Antrian Ke `{position}`\n" \ 1061 | + f"🎧 **Request Dari:** {r_by.mention}", 1062 | reply_markup=keyboard, 1063 | ) 1064 | os.remove("final.png") 1065 | 1066 | else: 1067 | que[chat_id] = [] 1068 | qeue = que.get(chat_id) 1069 | s_name = title 1070 | try: 1071 | r_by = cb.message.reply_to_message.from_user 1072 | except: 1073 | r_by = cb.message.from_user 1074 | loc = file_path 1075 | appendable = [s_name, r_by, loc] 1076 | qeue.append(appendable) 1077 | 1078 | callsmusic.pytgcalls.join_group_call(chat_id, file_path) 1079 | await cb.message.delete() 1080 | await b.send_photo(chat_id, 1081 | photo="final.png", 1082 | caption = f"🏷 **Judul:** [{title[:30]}]({url})\n⏱ **Durasi:** {duration}\n💡 **Status:** Sedang Memutar\n" \ 1083 | + f"🎧 **Request Dari:** {r_by.mention}", 1084 | reply_markup=keyboard, 1085 | ) 1086 | os.remove("final.png") 1087 | 1088 | # Have u read all. If read RESPECT :-) 1089 | -------------------------------------------------------------------------------- /GeezProject/modules/pmpermit.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | from pyrogram import Client 18 | import asyncio 19 | from GeezProject.config import SUDO_USERS, PMPERMIT, OWNER, PROJECT_NAME, UPDATES_CHANNEL, SUPPORT_GROUP 20 | from pyrogram import filters 21 | from pyrogram.types import Message 22 | from GeezProject.services.callsmusic.callsmusic import client as USER 23 | 24 | PMSET =True 25 | pchats = [] 26 | 27 | @USER.on_message(filters.text & filters.private & ~filters.me & ~filters.bot) 28 | async def pmPermit(client: USER, message: Message): 29 | if PMPERMIT == "ENABLE": 30 | if PMSET: 31 | chat_id = message.chat.id 32 | if chat_id in pchats: 33 | return 34 | await USER.send_message( 35 | message.chat.id, 36 | f"Halo, Saya adalah **Layanan Asistant {PROJECT_NAME}.**\n\n ❗️ **Rules:**\n - Jangan Spam Pesan disini\n - Jangan Spam Lagu Biar Ga Error\n - Tutorial Cara Menggunakan bot Lihat di @{UPDATES_CHANNEL} \n\n 👉 **KIRIM LINK INVITE ATAU USERNAME GRUP, JIKA ASSISTANT TIDAK DAPAT BERGABUNG DENGAN GRUP ANDA.**\n\n ⛑ **Group Support :** @{SUPPORT_GROUP} - **Owner** {OWNER}\n\n", 37 | ) 38 | return 39 | 40 | 41 | 42 | @Client.on_message(filters.command(["/pmpermit"])) 43 | async def bye(client: Client, message: Message): 44 | if message.from_user.id in SUDO_USERS: 45 | global PMSET 46 | text = message.text.split(" ", 1) 47 | queryy = text[1] 48 | if queryy == "on": 49 | PMSET = True 50 | await message.reply_text("Pmpermit turned on") 51 | return 52 | if queryy == "off": 53 | PMSET = None 54 | await message.reply_text("Pmpermit turned off") 55 | return 56 | 57 | @USER.on_message(filters.text & filters.private & filters.me) 58 | async def autopmPermiat(client: USER, message: Message): 59 | chat_id = message.chat.id 60 | if not chat_id in pchats: 61 | pchats.append(chat_id) 62 | await message.reply_text("Approoved to PM due to outgoing messages") 63 | return 64 | message.continue_propagation() 65 | 66 | @USER.on_message(filters.command("yes", [".", ""]) & filters.me & filters.private) 67 | async def pmPermiat(client: USER, message: Message): 68 | chat_id = message.chat.id 69 | if not chat_id in pchats: 70 | pchats.append(chat_id) 71 | await message.reply_text("Approoved to PM") 72 | return 73 | message.continue_propagation() 74 | 75 | 76 | @USER.on_message(filters.command("no", [".", ""]) & filters.me & filters.private) 77 | async def rmpmPermiat(client: USER, message: Message): 78 | chat_id = message.chat.id 79 | if chat_id in pchats: 80 | pchats.remove(chat_id) 81 | await message.reply_text("Dispprooved to PM") 82 | return 83 | message.continue_propagation() 84 | -------------------------------------------------------------------------------- /GeezProject/modules/private.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | import logging 18 | from GeezProject.modules.msg import Messages as tr 19 | from pyrogram import Client, filters 20 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 21 | from GeezProject.config import SOURCE_CODE,ASSISTANT_NAME,PROJECT_NAME,SUPPORT_GROUP,UPDATES_CHANNEL,BOT_USERNAME, OWNER 22 | logging.basicConfig(level=logging.INFO) 23 | 24 | @Client.on_message( 25 | filters.command("start") 26 | & filters.private 27 | & ~ filters.edited 28 | ) 29 | async def start_(client: Client, message: Message): 30 | await message.reply_text( 31 | f"""Haii {message.from_user.first_name} saya adalah {PROJECT_NAME}\n 32 | Saya Adalah Bot Music Group, Yang dapat Memutar Lagu di Voice Chat Group Anda Dengan Mudah 33 | Saya Memiliki Banyak Fitur Seperti : 34 | • Memutar Musik. 35 | • Mendownload Lagu. 36 | • Mencari Lagu Yang ingin di Putar atau di Download. 37 | • Gunakan Perintah » /help « untuk Mengetahui Fitur Lengkapnya 38 | 39 | 📌 Special Thanks To : {OWNER} 40 | 41 | Ingin Menambahkan Saya ke Grup Anda? Tambahkan Saya Ke Group Anda! 42 | """, 43 | 44 | # Edit Yang Seharusnya Lu Edit Aja:D 45 | # Tapi Jangan di Hapus Special Thanks To nya Yaaa :'D 46 | 47 | reply_markup=InlineKeyboardMarkup( 48 | [ 49 | [ 50 | InlineKeyboardButton( 51 | "➕ Tambahkan saya ke Grup Anda ➕", url=f"https://t.me/{BOT_USERNAME}?startgroup=true")], 52 | [ 53 | InlineKeyboardButton( 54 | "💬 Channel Updates", url=f"https://t.me/{UPDATES_CHANNEL}"), 55 | InlineKeyboardButton( 56 | "🎈 Group Support", url=f"https://t.me/{SUPPORT_GROUP}") 57 | ],[ 58 | InlineKeyboardButton( 59 | "🛠 Source Code 🛠", url=f"https://{SOURCE_CODE}") 60 | ],[ 61 | InlineKeyboardButton( 62 | "🎁 Donate", url=f"https://t.me/VckyouuBitch") 63 | ] 64 | ] 65 | ), 66 | reply_to_message_id=message.message_id 67 | ) 68 | 69 | @Client.on_message(filters.private & filters.incoming & filters.command(['help'])) 70 | def _help(client, message): 71 | client.send_message(chat_id = message.chat.id, 72 | text = tr.HELP_MSG[1], 73 | parse_mode="markdown", 74 | disable_web_page_preview=True, 75 | disable_notification=True, 76 | reply_markup = InlineKeyboardMarkup(map(1)), 77 | reply_to_message_id = message.message_id 78 | ) 79 | 80 | help_callback_filter = filters.create(lambda _, __, query: query.data.startswith('help+')) 81 | 82 | @Client.on_callback_query(help_callback_filter) 83 | def help_answer(client, callback_query): 84 | chat_id = callback_query.from_user.id 85 | disable_web_page_preview=True 86 | message_id = callback_query.message.message_id 87 | msg = int(callback_query.data.split('+')[1]) 88 | client.edit_message_text(chat_id=chat_id, message_id=message_id, 89 | text=tr.HELP_MSG[msg], reply_markup=InlineKeyboardMarkup(map(msg)) 90 | ) 91 | 92 | 93 | def map(pos): 94 | if(pos==1): 95 | button = [ 96 | [InlineKeyboardButton(text = 'Next »', callback_data = "help+2")] 97 | ] 98 | elif(pos==len(tr.HELP_MSG)-1): 99 | url = f"https://t.me/{SUPPORT_GROUP}" 100 | button = [ 101 | [InlineKeyboardButton("➕ Tambahkan saya ke Grup Anda ➕", url=f"https://t.me/{BOT_USERNAME}?startgroup=true")], 102 | [InlineKeyboardButton(text = '💬 Channel Updates', url=f"https://t.me/{UPDATES_CHANNEL}"), 103 | InlineKeyboardButton(text = '🔰 Group Support', url=f"https://t.me/{SUPPORT_GROUP}")], 104 | [InlineKeyboardButton(text = '🛠 Source Code 🛠', url=f"https://{SOURCE_CODE}")], 105 | [InlineKeyboardButton(text = '«', callback_data = f"help+{pos-1}")] 106 | ] 107 | else: 108 | button = [ 109 | [ 110 | InlineKeyboardButton(text = '«', callback_data = f"help+{pos-1}"), 111 | InlineKeyboardButton(text = '»', callback_data = f"help+{pos+1}") 112 | ], 113 | ] 114 | return button 115 | 116 | 117 | @Client.on_message( 118 | filters.command("start") 119 | & filters.group 120 | & ~ filters.edited 121 | ) 122 | async def start(client: Client, message: Message): 123 | await message.reply_text( 124 | "💁🏻‍♂️ **Apakah Anda ingin mencari Link YouTube?**", 125 | reply_markup=InlineKeyboardMarkup( 126 | [ 127 | [ 128 | InlineKeyboardButton( 129 | "✅ Ya", switch_inline_query_current_chat="" 130 | ), 131 | InlineKeyboardButton( 132 | "❌ Tidak ", callback_data="close" 133 | ) 134 | ] 135 | ] 136 | ) 137 | ) 138 | 139 | 140 | @Client.on_message( 141 | filters.command("help") 142 | & filters.group 143 | & ~ filters.edited 144 | ) 145 | async def help(client: Client, message: Message): 146 | await message.reply_text( 147 | """**Klik Tombol dibawah untuk Melihat Cara Menggunakan Bot**""", 148 | reply_markup=InlineKeyboardMarkup( 149 | [ 150 | [ 151 | InlineKeyboardButton( 152 | "📜 Cara Menggunakan BOT 📜", url="https://t.me/Vckyouuu/32" 153 | ) 154 | ] 155 | ] 156 | ), 157 | ) 158 | 159 | 160 | @Client.on_message( 161 | filters.command("reload") 162 | & filters.group 163 | & ~ filters.edited 164 | ) 165 | async def reload(client: Client, message: Message): 166 | await message.reply_text("""✅ Bot **berhasil dimulai ulang!**\n\n• **Daftar admin** telah **diperbarui**""", 167 | reply_markup=InlineKeyboardMarkup( 168 | [ 169 | [ 170 | InlineKeyboardButton( 171 | "Group Support", url=f"https://t.me/GeezSupportGroup" 172 | ), 173 | InlineKeyboardButton( 174 | "Created By", url=f"https://t.me/VckyouuBitch" 175 | ) 176 | ] 177 | ] 178 | ) 179 | ) 180 | 181 | -------------------------------------------------------------------------------- /GeezProject/modules/song.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from __future__ import unicode_literals 19 | 20 | import asyncio 21 | import math 22 | import os 23 | import time 24 | from random import randint 25 | from urllib.parse import urlparse 26 | 27 | import aiofiles 28 | import aiohttp 29 | import requests 30 | import wget 31 | import youtube_dl 32 | from pyrogram import Client, filters 33 | from pyrogram.errors import FloodWait, MessageNotModified 34 | from pyrogram.types import Message 35 | from youtube_search import YoutubeSearch 36 | from youtubesearchpython import SearchVideos 37 | 38 | from GeezProject.config import DURATION_LIMIT 39 | from GeezProject.modules.play import arq 40 | 41 | 42 | @Client.on_message(filters.command("song") & ~filters.channel) 43 | def song(client, message): 44 | 45 | user_id = message.from_user.id 46 | user_name = message.from_user.first_name 47 | rpk = "[" + user_name + "](tg://user?id=" + str(user_id) + ")" 48 | 49 | query = "" 50 | for i in message.command[1:]: 51 | query += " " + str(i) 52 | print(query) 53 | m = message.reply("🔎 Finding the song...") 54 | ydl_opts = {"format": "bestaudio[ext=m4a]"} 55 | try: 56 | results = YoutubeSearch(query, max_results=1).to_dict() 57 | link = f"https://youtube.com{results[0]['url_suffix']}" 58 | # print(results) 59 | title = results[0]["title"][:40] 60 | thumbnail = results[0]["thumbnails"][0] 61 | thumb_name = f"thumb{title}.jpg" 62 | thumb = requests.get(thumbnail, allow_redirects=True) 63 | open(thumb_name, "wb").write(thumb.content) 64 | 65 | duration = results[0]["duration"] 66 | results[0]["url_suffix"] 67 | results[0]["views"] 68 | 69 | except Exception as e: 70 | m.edit("❌ Found Nothing.\n\nTry another keywork or maybe spell it properly.") 71 | print(str(e)) 72 | return 73 | m.edit("Downloading the song ") 74 | try: 75 | with youtube_dl.YoutubeDL(ydl_opts) as ydl: 76 | info_dict = ydl.extract_info(link, download=False) 77 | audio_file = ydl.prepare_filename(info_dict) 78 | ydl.process_info(info_dict) 79 | rep = "**🎵 Diunggah Oleh @GeezProjects **" 80 | secmul, dur, dur_arr = 1, 0, duration.split(":") 81 | for i in range(len(dur_arr) - 1, -1, -1): 82 | dur += int(dur_arr[i]) * secmul 83 | secmul *= 60 84 | message.reply_audio( 85 | audio_file, 86 | caption=rep, 87 | thumb=thumb_name, 88 | parse_mode="md", 89 | title=title, 90 | duration=dur, 91 | ) 92 | m.delete() 93 | except Exception as e: 94 | m.edit("❌ Error") 95 | print(e) 96 | 97 | try: 98 | os.remove(audio_file) 99 | os.remove(thumb_name) 100 | except Exception as e: 101 | print(e) 102 | 103 | 104 | def get_text(message: Message) -> [None, str]: 105 | text_to_return = message.text 106 | if message.text is None: 107 | return None 108 | if " " in text_to_return: 109 | try: 110 | return message.text.split(None, 1)[1] 111 | except IndexError: 112 | return None 113 | else: 114 | return None 115 | 116 | 117 | def humanbytes(size): 118 | if not size: 119 | return "" 120 | power = 2 ** 10 121 | raised_to_pow = 0 122 | dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} 123 | while size > power: 124 | size /= power 125 | raised_to_pow += 1 126 | return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B" 127 | 128 | 129 | async def progress(current, total, message, start, type_of_ps, file_name=None): 130 | now = time.time() 131 | diff = now - start 132 | if round(diff % 10.00) == 0 or current == total: 133 | percentage = current * 100 / total 134 | speed = current / diff 135 | elapsed_time = round(diff) * 1000 136 | if elapsed_time == 0: 137 | return 138 | time_to_completion = round((total - current) / speed) * 1000 139 | estimated_total_time = elapsed_time + time_to_completion 140 | progress_str = "{0}{1} {2}%\n".format( 141 | "".join(["🔴" for i in range(math.floor(percentage / 10))]), 142 | "".join(["🔘" for i in range(10 - math.floor(percentage / 10))]), 143 | round(percentage, 2), 144 | ) 145 | tmp = progress_str + "{0} of {1}\nETA: {2}".format( 146 | humanbytes(current), humanbytes(total), time_formatter(estimated_total_time) 147 | ) 148 | if file_name: 149 | try: 150 | await message.edit( 151 | "{}\n**File Name:** `{}`\n{}".format(type_of_ps, file_name, tmp) 152 | ) 153 | except FloodWait as e: 154 | await asyncio.sleep(e.x) 155 | except MessageNotModified: 156 | pass 157 | else: 158 | try: 159 | await message.edit("{}\n{}".format(type_of_ps, tmp)) 160 | except FloodWait as e: 161 | await asyncio.sleep(e.x) 162 | except MessageNotModified: 163 | pass 164 | 165 | 166 | def get_user(message: Message, text: str) -> [int, str, None]: 167 | if text is None: 168 | asplit = None 169 | else: 170 | asplit = text.split(" ", 1) 171 | user_s = None 172 | reason_ = None 173 | if message.reply_to_message: 174 | user_s = message.reply_to_message.from_user.id 175 | reason_ = text if text else None 176 | elif asplit is None: 177 | return None, None 178 | elif len(asplit[0]) > 0: 179 | user_s = int(asplit[0]) if asplit[0].isdigit() else asplit[0] 180 | if len(asplit) == 2: 181 | reason_ = asplit[1] 182 | return user_s, reason_ 183 | 184 | 185 | def get_readable_time(seconds: int) -> int: 186 | count = 0 187 | ping_time = "" 188 | time_list = [] 189 | time_suffix_list = ["s", "m", "h", "days"] 190 | 191 | while count < 4: 192 | count += 1 193 | if count < 3: 194 | remainder, result = divmod(seconds, 60) 195 | else: 196 | remainder, result = divmod(seconds, 24) 197 | if seconds == 0 and remainder == 0: 198 | break 199 | time_list.append(int(result)) 200 | seconds = int(remainder) 201 | 202 | for x in range(len(time_list)): 203 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 204 | if len(time_list) == 4: 205 | ping_time += time_list.pop() + ", " 206 | 207 | time_list.reverse() 208 | ping_time += ":".join(time_list) 209 | 210 | return ping_time 211 | 212 | 213 | def time_formatter(milliseconds: int) -> str: 214 | seconds, milliseconds = divmod(int(milliseconds), 1000) 215 | minutes, seconds = divmod(seconds, 60) 216 | hours, minutes = divmod(minutes, 60) 217 | days, hours = divmod(hours, 24) 218 | tmp = ( 219 | ((str(days) + " day(s), ") if days else "") 220 | + ((str(hours) + " hour(s), ") if hours else "") 221 | + ((str(minutes) + " minute(s), ") if minutes else "") 222 | + ((str(seconds) + " second(s), ") if seconds else "") 223 | + ((str(milliseconds) + " millisecond(s), ") if milliseconds else "") 224 | ) 225 | return tmp[:-2] 226 | 227 | 228 | ydl_opts = { 229 | "format": "bestaudio/best", 230 | "writethumbnail": True, 231 | "postprocessors": [ 232 | { 233 | "key": "FFmpegExtractAudio", 234 | "preferredcodec": "mp3", 235 | "preferredquality": "192", 236 | } 237 | ], 238 | } 239 | 240 | 241 | def get_file_extension_from_url(url): 242 | url_path = urlparse(url).path 243 | basename = os.path.basename(url_path) 244 | return basename.split(".")[-1] 245 | 246 | 247 | # Funtion To Download Song 248 | async def download_song(url): 249 | song_name = f"{randint(6969, 6999)}.mp3" 250 | async with aiohttp.ClientSession() as session: 251 | async with session.get(url) as resp: 252 | if resp.status == 200: 253 | f = await aiofiles.open(song_name, mode="wb") 254 | await f.write(await resp.read()) 255 | await f.close() 256 | return song_name 257 | 258 | 259 | is_downloading = False 260 | 261 | 262 | def time_to_seconds(time): 263 | stringt = str(time) 264 | return sum(int(x) * 60 ** i for i, x in enumerate(reversed(stringt.split(":")))) 265 | 266 | 267 | @Client.on_message(filters.command("saavn") & ~filters.edited) 268 | async def jssong(_, message): 269 | global is_downloading 270 | if len(message.command) < 2: 271 | await message.reply_text("/saavn requires an argument.") 272 | return 273 | if is_downloading: 274 | await message.reply_text( 275 | "Another download is in progress, try again after sometime." 276 | ) 277 | return 278 | is_downloading = True 279 | text = message.text.split(None, 1)[1] 280 | query = text.replace(" ", "%20") 281 | m = await message.reply_text("Searching...") 282 | try: 283 | songs = await arq.saavn(query) 284 | if not songs.ok: 285 | await message.reply_text(songs.result) 286 | return 287 | sname = songs.result[0].song 288 | slink = songs.result[0].media_url 289 | ssingers = songs.result[0].singers 290 | await m.edit("Downloading") 291 | song = await download_song(slink) 292 | await m.edit("Uploading") 293 | await message.reply_audio(audio=song, title=sname, performer=ssingers) 294 | os.remove(song) 295 | await m.delete() 296 | except Exception as e: 297 | is_downloading = False 298 | await m.edit(str(e)) 299 | return 300 | is_downloading = False 301 | 302 | 303 | # Deezer Music 304 | 305 | 306 | @Client.on_message(filters.command("deezer") & ~filters.edited) 307 | async def deezsong(_, message): 308 | global is_downloading 309 | if len(message.command) < 2: 310 | await message.reply_text("/deezer requires an argument.") 311 | return 312 | if is_downloading: 313 | await message.reply_text( 314 | "Another download is in progress, try again after sometime." 315 | ) 316 | return 317 | is_downloading = True 318 | text = message.text.split(None, 1)[1] 319 | query = text.replace(" ", "%20") 320 | m = await message.reply_text("Searching...") 321 | try: 322 | songs = await arq.deezer(query, 1) 323 | if not songs.ok: 324 | await message.reply_text(songs.result) 325 | return 326 | title = songs.result[0].title 327 | url = songs.result[0].url 328 | artist = songs.result[0].artist 329 | await m.edit("Downloading") 330 | song = await download_song(url) 331 | await m.edit("Uploading") 332 | await message.reply_audio(audio=song, title=title, performer=artist) 333 | os.remove(song) 334 | await m.delete() 335 | except Exception as e: 336 | is_downloading = False 337 | await m.edit(str(e)) 338 | return 339 | is_downloading = False 340 | 341 | 342 | @Client.on_message(filters.command(["vsong", "video"])) 343 | async def ytmusic(client, message: Message): 344 | global is_downloading 345 | if is_downloading: 346 | await message.reply_text( 347 | "Another download is in progress, try again after sometime." 348 | ) 349 | return 350 | 351 | urlissed = get_text(message) 352 | 353 | pablo = await client.send_message( 354 | message.chat.id, f"`Getting {urlissed} From Youtube Servers. Please Wait.`" 355 | ) 356 | if not urlissed: 357 | await pablo.edit("Invalid Command Syntax, Please Check Help Menu To Know More!") 358 | return 359 | 360 | search = SearchVideos(f"{urlissed}", offset=1, mode="dict", max_results=1) 361 | mi = search.result() 362 | mio = mi["search_result"] 363 | mo = mio[0]["link"] 364 | thum = mio[0]["title"] 365 | fridayz = mio[0]["id"] 366 | thums = mio[0]["channel"] 367 | kekme = f"https://img.youtube.com/vi/{fridayz}/hqdefault.jpg" 368 | await asyncio.sleep(0.6) 369 | url = mo 370 | sedlyf = wget.download(kekme) 371 | opts = { 372 | "format": "best", 373 | "addmetadata": True, 374 | "key": "FFmpegMetadata", 375 | "prefer_ffmpeg": True, 376 | "geo_bypass": True, 377 | "nocheckcertificate": True, 378 | "postprocessors": [{"key": "FFmpegVideoConvertor", "preferedformat": "mp4"}], 379 | "outtmpl": "%(id)s.mp4", 380 | "logtostderr": False, 381 | "quiet": True, 382 | } 383 | try: 384 | is_downloading = True 385 | with youtube_dl.YoutubeDL(opts) as ytdl: 386 | infoo = ytdl.extract_info(url, False) 387 | duration = round(infoo["duration"] / 60) 388 | 389 | if duration > DURATION_LIMIT: 390 | await pablo.edit( 391 | f"❌ Videos longer than {DURATION_LIMIT} minute(s) aren't allowed, the provided video is {duration} minute(s)" 392 | ) 393 | is_downloading = False 394 | return 395 | ytdl_data = ytdl.extract_info(url, download=True) 396 | 397 | except Exception: 398 | # await pablo.edit(event, f"**Failed To Download** \n**Error :** `{str(e)}`") 399 | is_downloading = False 400 | return 401 | 402 | c_time = time.time() 403 | file_stark = f"{ytdl_data['id']}.mp4" 404 | capy = f"**Video Name ➠** `{thum}` \n**Requested For :** `{urlissed}` \n**Channel :** `{thums}` \n**Link :** `{mo}`" 405 | await client.send_video( 406 | message.chat.id, 407 | video=open(file_stark, "rb"), 408 | duration=int(ytdl_data["duration"]), 409 | file_name=str(ytdl_data["title"]), 410 | thumb=sedlyf, 411 | caption=capy, 412 | supports_streaming=True, 413 | progress=progress, 414 | progress_args=( 415 | pablo, 416 | c_time, 417 | f"`Uploading {urlissed} Song From YouTube Music!`", 418 | file_stark, 419 | ), 420 | ) 421 | await pablo.delete() 422 | is_downloading = False 423 | for files in (sedlyf, file_stark): 424 | if files and os.path.exists(files): 425 | os.remove(files) 426 | -------------------------------------------------------------------------------- /GeezProject/modules/userbotjoin.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from pyrogram import Client, filters 19 | from pyrogram.errors import UserAlreadyParticipant 20 | import asyncio 21 | from GeezProject.helpers.decorators import authorized_users_only, errors 22 | from GeezProject.services.callsmusic.callsmusic import client as USER 23 | from GeezProject.config import SUDO_USERS 24 | 25 | 26 | @Client.on_message(filters.command(["userbotjoin"]) & ~filters.private & ~filters.bot) 27 | @authorized_users_only 28 | @errors 29 | async def addchannel(client, message): 30 | chid = message.chat.id 31 | try: 32 | invitelink = await client.export_chat_invite_link(chid) 33 | except: 34 | await message.reply_text( 35 | "Add me as admin of yor group first", 36 | ) 37 | return 38 | 39 | try: 40 | user = await USER.get_me() 41 | except: 42 | user.first_name = "DaisyMusic" 43 | 44 | try: 45 | await USER.join_chat(invitelink) 46 | await USER.send_message(message.chat.id, "I joined here as you requested") 47 | except UserAlreadyParticipant: 48 | await message.reply_text( 49 | "helper already in your chat", 50 | ) 51 | except Exception as e: 52 | print(e) 53 | await message.reply_text( 54 | f"🛑 Flood Wait Error 🛑 \n User {user.first_name} couldn't join your group due to heavy join requests for userbot! Make sure user is not banned in group." 55 | "\n\nOr manually add Asisstant to your Group and try again", 56 | ) 57 | return 58 | await message.reply_text( 59 | "helper userbot joined your chat", 60 | ) 61 | 62 | 63 | @USER.on_message(filters.group & filters.command(["userbotleave"])) 64 | @authorized_users_only 65 | async def rem(USER, message): 66 | try: 67 | await USER.leave_chat(message.chat.id) 68 | except: 69 | await message.reply_text( 70 | f"User couldn't leave your group! May be floodwaits." 71 | "\n\nOr manually kick me from to your Group", 72 | ) 73 | return 74 | 75 | @Client.on_message(filters.command(["userbotleaveall"])) 76 | async def bye(client, message): 77 | if message.from_user.id in SUDO_USERS: 78 | left=0 79 | failed=0 80 | lol = await message.reply("Assistant Leaving all chats") 81 | async for dialog in USER.iter_dialogs(): 82 | try: 83 | await USER.leave_chat(dialog.chat.id) 84 | left = left+1 85 | await lol.edit(f"Assistant leaving... Left: {left} chats. Failed: {failed} chats.") 86 | except: 87 | failed=failed+1 88 | await lol.edit(f"Assistant leaving... Left: {left} chats. Failed: {failed} chats.") 89 | await asyncio.sleep(0.7) 90 | await client.send_message(message.chat.id, f"Left {left} chats. Failed {failed} chats.") 91 | 92 | 93 | @Client.on_message(filters.command(["userbotjoinchannel","ubjoinc"]) & ~filters.private & ~filters.bot) 94 | @authorized_users_only 95 | @errors 96 | async def addcchannel(client, message): 97 | try: 98 | conchat = await client.get_chat(message.chat.id) 99 | conid = conchat.linked_chat.id 100 | chid = conid 101 | except: 102 | await message.reply("Is chat even linked") 103 | return 104 | chat_id = chid 105 | try: 106 | invitelink = await client.export_chat_invite_link(chid) 107 | except: 108 | await message.reply_text( 109 | "Add me as admin of yor channel first", 110 | ) 111 | return 112 | 113 | try: 114 | user = await USER.get_me() 115 | except: 116 | user.first_name = "GeezProject" 117 | 118 | try: 119 | await USER.join_chat(invitelink) 120 | await USER.send_message(message.chat.id, "I joined here as you requested") 121 | except UserAlreadyParticipant: 122 | await message.reply_text( 123 | "helper already in your channel", 124 | ) 125 | return 126 | except Exception as e: 127 | print(e) 128 | await message.reply_text( 129 | f"🛑 Flood Wait Error 🛑 \n User {user.first_name} couldn't join your channel due to heavy join requests for userbot! Make sure user is not banned in channel." 130 | "\n\nOr manually add Assistant to your Group and try again", 131 | ) 132 | return 133 | await message.reply_text( 134 | "helper userbot joined your channel", 135 | ) 136 | 137 | -------------------------------------------------------------------------------- /GeezProject/modules/ytsearch.py: -------------------------------------------------------------------------------- 1 | # Daisyxmusic (Telegram bot project ) 2 | # Copyright (C) 2021 Inukaasith 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | # the logging things 19 | import logging 20 | 21 | from pyrogram import Client as app 22 | from pyrogram.types import Message 23 | from youtube_search import YoutubeSearch 24 | 25 | logging.basicConfig( 26 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" 27 | ) 28 | logger = logging.getLogger(__name__) 29 | 30 | import pyrogram 31 | 32 | logging.getLogger("pyrogram").setLevel(logging.WARNING) 33 | 34 | 35 | @app.on_message(pyrogram.filters.command(["search"])) 36 | async def ytsearch(_, message: Message): 37 | try: 38 | if len(message.command) < 2: 39 | await message.reply_text("/search needs an argument!") 40 | return 41 | query = message.text.split(None, 1)[1] 42 | m = await message.reply_text("Searching....") 43 | results = YoutubeSearch(query, max_results=4).to_dict() 44 | i = 0 45 | text = "" 46 | while i < 4: 47 | text += f"Title - {results[i]['title']}\n" 48 | text += f"Duration - {results[i]['duration']}\n" 49 | text += f"Views - {results[i]['views']}\n" 50 | text += f"Channel - {results[i]['channel']}\n" 51 | text += f"https://youtube.com{results[i]['url_suffix']}\n\n" 52 | i += 1 53 | await m.edit(text, disable_web_page_preview=True) 54 | except Exception as e: 55 | await message.reply_text(str(e)) 56 | -------------------------------------------------------------------------------- /GeezProject/services/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /GeezProject/services/callsmusic/__init__.py: -------------------------------------------------------------------------------- 1 | from GeezProject.services.queues import queues 2 | from GeezProject.services.callsmusic.callsmusic import pytgcalls, run 3 | 4 | __all__ = ["queues", "pytgcalls", "run"] 5 | -------------------------------------------------------------------------------- /GeezProject/services/callsmusic/callsmusic.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from pyrogram import Client 19 | from pytgcalls import PyTgCalls 20 | 21 | from GeezProject.config import API_HASH, API_ID, SESSION_NAME 22 | from GeezProject.services.queues import queues 23 | 24 | client = Client(SESSION_NAME, API_ID, API_HASH) 25 | pytgcalls = PyTgCalls(client) 26 | 27 | 28 | @pytgcalls.on_stream_end() 29 | def on_stream_end(chat_id: int) -> None: 30 | queues.task_done(chat_id) 31 | 32 | if queues.is_empty(chat_id): 33 | pytgcalls.leave_group_call(chat_id) 34 | else: 35 | pytgcalls.change_stream(chat_id, queues.get(chat_id)["file"]) 36 | 37 | 38 | run = pytgcalls.run 39 | -------------------------------------------------------------------------------- /GeezProject/services/converter/__init__.py: -------------------------------------------------------------------------------- 1 | from os import listdir, mkdir 2 | 3 | if "raw_files" not in listdir(): 4 | mkdir("raw_files") 5 | 6 | from GeezProject.services.converter.converter import convert 7 | 8 | __all__ = ["convert"] 9 | -------------------------------------------------------------------------------- /GeezProject/services/converter/converter.py: -------------------------------------------------------------------------------- 1 | # Calls Music 1 - Telegram bot for streaming audio in group calls 2 | # Copyright (C) 2021 Roj Serbest 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | import asyncio 19 | from os import path 20 | 21 | from GeezProject.helpers.errors import FFmpegReturnCodeError 22 | 23 | 24 | async def convert(file_path: str) -> str: 25 | out = path.basename(file_path) 26 | out = out.split(".") 27 | out[-1] = "raw" 28 | out = ".".join(out) 29 | out = path.basename(out) 30 | out = path.join("raw_files", out) 31 | 32 | if path.isfile(out): 33 | return out 34 | try: 35 | proc = await asyncio.create_subprocess_shell( 36 | f"ffmpeg -y -i {file_path} -f s16le -ac 1 -ar 48000 -acodec pcm_s16le {out}", 37 | asyncio.subprocess.PIPE, 38 | stderr=asyncio.subprocess.PIPE, 39 | ) 40 | 41 | await proc.communicate() 42 | 43 | if proc.returncode != 0: 44 | raise FFmpegReturnCodeError("FFmpeg did not return 0") 45 | 46 | return out 47 | except: 48 | raise FFmpegReturnCodeError("FFmpeg did not return 0") 49 | -------------------------------------------------------------------------------- /GeezProject/services/downloaders/__init__.py: -------------------------------------------------------------------------------- 1 | from GeezProject.services.downloaders import youtube 2 | 3 | __all__ = ["youtube"] 4 | -------------------------------------------------------------------------------- /GeezProject/services/downloaders/youtube.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | 3 | from youtube_dl import YoutubeDL 4 | 5 | from GeezProject.config import DURATION_LIMIT 6 | from GeezProject.helpers.errors import DurationLimitError 7 | 8 | ydl_opts = { 9 | "format": "bestaudio/best", 10 | "verbose": True, 11 | "geo-bypass": True, 12 | "nocheckcertificate": True, 13 | "outtmpl": "downloads/%(id)s.%(ext)s", 14 | } 15 | 16 | ydl = YoutubeDL(ydl_opts) 17 | 18 | 19 | def download(url: str) -> str: 20 | info = ydl.extract_info(url, False) 21 | duration = round(info["duration"] / 120) 22 | 23 | if duration > DURATION_LIMIT: 24 | raise DurationLimitError( 25 | f"🛑 Videos longer than {DURATION_LIMIT} minute(s) aren't allowed, the provided video is {duration} minute(s)" 26 | ) 27 | try: 28 | ydl.download([url]) 29 | except: 30 | raise DurationLimitError( 31 | f"🛑 Videos longer than {DURATION_LIMIT} minute(s) aren't allowed, the provided video is {duration} minute(s)" 32 | ) 33 | return path.join("downloads", f"{info['id']}.{info['ext']}") 34 | -------------------------------------------------------------------------------- /GeezProject/services/queues/__init__.py: -------------------------------------------------------------------------------- 1 | from GeezProject.services.queues.queues import clear, get, is_empty, put, task_done 2 | 3 | __all__ = ["clear", "get", "is_empty", "put", "task_done"] 4 | -------------------------------------------------------------------------------- /GeezProject/services/queues/queues.py: -------------------------------------------------------------------------------- 1 | from asyncio import Queue 2 | from asyncio import QueueEmpty as Empty 3 | from typing import Dict 4 | 5 | 6 | queues: Dict[int, Queue] = {} 7 | 8 | 9 | async def put(chat_id: int, **kwargs) -> int: 10 | if chat_id not in queues: 11 | queues[chat_id] = Queue() 12 | await queues[chat_id].put({**kwargs}) 13 | return queues[chat_id].qsize() 14 | 15 | 16 | def get(chat_id: int) -> Dict[str, str]: 17 | if chat_id in queues: 18 | try: 19 | return queues[chat_id].get_nowait() 20 | except Empty: 21 | return None 22 | 23 | 24 | def is_empty(chat_id: int) -> bool: 25 | if chat_id in queues: 26 | return queues[chat_id].empty() 27 | return True 28 | 29 | 30 | def task_done(chat_id: int): 31 | if chat_id in queues: 32 | try: 33 | queues[chat_id].task_done() 34 | except ValueError: 35 | pass 36 | 37 | 38 | def clear(chat_id: int): 39 | if chat_id in queues: 40 | if queues[chat_id].empty(): 41 | raise Empty 42 | else: 43 | queues[chat_id].queue = [] 44 | raise Empty 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

🎵 Geez Music Projects 🎵

2 | 3 | ### A bot that can play music on Telegram Group and Channel Voice Chats 4 | #### POWERED BY [PYTGCALLS](https://github.com/pytgcalls/pytgcalls) 5 | ### Available on telegram as [@GeezMusicBot](https://t.me/GeezMusicBot) 6 | 7 |

8 | 9 |

10 |

Features

11 | 12 | - Thumbnail Support 13 | - Playlist Support 14 | - Current playback support 15 | - Showing track names when skipping 16 | - Zero downtime, Fully Stable 17 | - Deezer,Youtube & Saavn playback support 18 | - Settings panel 19 | - Control with buttons 20 | - Userbot auto join 21 | 22 | ## 🚀 Deployment 23 | 24 | ### ⚔ Self-hosting (For Devs) 25 | ```sh 26 | # Install Git First (apt-instll git) 27 | $ git clone https://github.com/vckyou/Geez-MusicProject 28 | $ cd Geez-MusicProject 29 | # Upgrade sources 30 | # Install All Requirements 31 | $ pip(3) install -r requirements.txt 32 | # Rename example.env to local.env and fill 33 | $ npm i -g npm 34 | # Start Bot 35 | $ python(3) -m GeezProject 36 | ``` 37 | 38 | ### 💜 Heroku 39 | 40 | # CLOSED DEPLOY!!! 41 | 42 | Get pyrogram (p) `SESSION` from here: 43 | [![Run on Repl.it](https://repl.it/badge/github/vckyou/PyrogramString)](https://repl.it/@vckyou/PyrogramString?lite=1&outputonly=1) 44 | 45 | ### Commands for Group 🛠 46 | #### For all in group 47 | 48 | - `/playlist` - Show now playing list 49 | - `/current` - Show now playing 50 | - `/song ` - download songs you want quickly 51 | - `/search ` - search videos on youtube with details 52 | - `/deezer ` - download songs you want quickly via deezer 53 | - `/saavn ` - download songs you want quickly via saavn 54 | - `/video ` - download videos you want quickly 55 | 56 | #### Admins only. 57 | - `/play ` - play song you requested 58 | - `/play ` - play replied file 59 | - `/dplay ` - play song you requested via deezer 60 | - `/splay ` - play song you requested via jio saavn 61 | - `/player` - open music player settings panel 62 | - `/pause` - pause song play 63 | - `/resume` - resume song play 64 | - `/skip` - play next song 65 | - `/end` - stop music play 66 | - `/userbotjoin` - invite assistant to your chat 67 | - `/userbotleave` - remove assistant from your chat 68 | - `/admincache` - Refresh admin list 69 | 70 | 71 | #### Pmpermit 72 | - `.yes` - approove someone to pm you 73 | - `.no` - disapproove someone to pm you 74 | + Sudo Users can execute any command in any groups 75 | 76 | ### Credits 77 | Don't edit this part 78 | 79 | #### Special Credits 80 | - [Rojserbest](http://github.com/rojserbes): Callsmusic Developer 81 | 82 | This bot is based on the original work done by [Rojserbest](http://github.com/rojserbest). Without his hardwork Geez Music won't exist. 83 | Geez-Project is a modified version of [Callsmusic](https://github.com/callsmusic/callsmusic) 84 | 85 | #### Contribtors 86 | - [Vicky](https://github.com/Vckyou): Dev 87 | - [InukaASiTH](https://github.com/InukaAsith): Dev 88 | - [Rojserbest](http://github.com/rojserbes): Dev 89 | - [Wrench](https://github.com/EverythingSuckz/): Dev 90 | - [QueenArzoo](https://github.com/QueenArzoo): Dev 91 | - [lucifeermorningstar](https://github.com/lucifeermorningstar): Dev 92 | - [AuraXNetwork](https://github.com/AuraXNetwork/AuraXMusicBot) 93 | - [Hamker Cat](https://github.com/thehamkercat/) 94 | - [Anjana-Ma](https://github.com/Anjana-Ma): 95 | - [ImJanindu](https://github.com/ImJanindu): 96 | - [Laky](https://github.com/Laky-64) & [Andrew](https://github.com/AndrewLaneX): PyTgCalls 97 | - [Original Repo owners](https://github.com/suprojects/CallsMusic) 98 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Geez Music", 3 | "description": "Open-Source bot to play songs in your Telegram's Group Voice Chat. Powered by PyTgCalls.", 4 | "keywords": ["music", "voicechat", "telegram"], 5 | "repository": "https://github.com/vckyou/Geez-MusicProject", 6 | "stack": "container", 7 | "buildpacks": [ 8 | { 9 | "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest" 10 | } 11 | ], 12 | "env": { 13 | "SESSION_NAME": { 14 | "description": "Masukan String Session Pyrogram (P) untuk assistant yang naik ke VCG", 15 | "required": true 16 | }, 17 | "BOT_TOKEN": { 18 | "description": "Masukan Bot Token dari @BotFather", 19 | "required": true 20 | }, 21 | "BOT_USERNAME": { 22 | "description": "Username bot Anda tanpa @", 23 | "required": true 24 | }, 25 | "BOT_NAME": { 26 | "description": "Nama Bot MusicPlayer Anda.", 27 | "required": false, 28 | "value": "" 29 | }, 30 | "SUPPORT_GROUP": { 31 | "description": "Grup dukungan MusicPlayer Anda Tanpa Menggunakan ==> @ <== [skip ini jika Anda tidak punya]", 32 | "required": false, 33 | "value": "GeezSupportGroup" 34 | }, 35 | "UPDATES_CHANNEL": { 36 | "description": "Updates Channel @ [skip ini jika Anda tidak punya]", 37 | "required": false, 38 | "value": "GeezProjects" 39 | }, 40 | "PROJECT_NAME": { 41 | "description": "Nama proyek MusicPlayer Anda.", 42 | "required": false, 43 | "value": "Geez Projects" 44 | }, 45 | "ASSISTANT_NAME": { 46 | "description": "Username Telegram asisten Music Player Anda tanpa @.", 47 | "required": true 48 | }, 49 | "OWNER": { 50 | "description": "Masukan Nama atau Username Telegram", 51 | "required": true 52 | }, 53 | "ARQ_API_KEY": { 54 | "description": "Ambil api dari bot telegram » t.me/ARQRobot atau @ARQRobot", 55 | "required": true 56 | }, 57 | "BG_IMAGE": { 58 | "description": "Custom Background Play Musik Gunakan Foto yang Transparant format .png / .jpg", 59 | "required": false, 60 | "value": "https://telegra.ph/file/7de6a24af6cfc049a9316.png" 61 | }, 62 | "API_ID": { 63 | "description": "Masukan APP ID Silahkan Ambil dari my.telegram.org/apps atau t.me/scrapmanbot", 64 | "required": true 65 | }, 66 | "API_HASH": { 67 | "description": "Masukan API HASH Silahkan Ambil dari my.telegram.org/apps atau t.me/scrapmanbot", 68 | "required": true 69 | }, 70 | "PMPERMIT": { 71 | "description": "Ubah untuk mematikan PMPERMIT", 72 | "required": false, 73 | "value": "ENABLE" 74 | }, 75 | "SUDO_USERS": { 76 | "description": "Daftar User ID Telegram pengguna dihitung sebagai admin di semua tempat (pemisah menggunakan space)", 77 | "required": true 78 | }, 79 | "DURATION_LIMIT": { 80 | "description": "Batas durasi audio maksimal untuk download (minutes).", 81 | "required": true, 82 | "value": "15" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /etc/font.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vckyou/Geez-MusicProjectOld/3e7c0513c14b9aec22b3c1f60f0270c2a940e0a3/etc/font.otf -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | SESSION_NAME=session # If you don't deploy with docker, keep it as is and if you do so it should be a session string generated by "python str.py" 2 | BOT_TOKEN=123456:abcdefghijklmnopqrstuv 3 | BOT_NAME=HELLBOT 4 | API_ID=123456 5 | API_HASH=abcdefghijklmnopqrstuv 6 | SUDO_USERS=1111 2222 # List of user IDs separated by space 7 | DURATION_LIMIT=10 # in minutes (default: 7) 8 | ARQ_API_KEY= 9 | UPDATES_CHANNEL=Vckyouuu 10 | BG_IMAGE=https://telegra.ph/file/fa2cdb8a14a26950da711.png 11 | 12 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | run: 5 | worker: python3 -m GeezProject 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram 2 | tgcrypto 3 | py-tgcalls==0.5.5 4 | python-dotenv 5 | youtube_dl 6 | youtube_search_python 7 | requests 8 | aiohttp 9 | aiofiles 10 | asyncio 11 | youtube_search 12 | search_engine_parser 13 | ffmpeg 14 | Pillow 15 | python-arq 16 | future 17 | psutil 18 | wget 19 | -------------------------------------------------------------------------------- /str.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from pyrogram import Client 4 | 5 | print("Enter your app information from my.telegram.org/apps below.") 6 | 7 | 8 | async def main(): 9 | async with Client( 10 | ":memory:", api_id=int(input("API ID:")), api_hash=input("API HASH:") 11 | ) as app: 12 | print(await app.export_session_string()) 13 | 14 | 15 | if __name__ == "__main__": 16 | loop = asyncio.get_event_loop() 17 | loop.run_until_complete(main()) 18 | --------------------------------------------------------------------------------