Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Requested runtime (python-3.8.2) is not available for this stack (heroku-20). #28

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b956d3b
Create app.json
TGExplore May 18, 2020
6ea3982
Update app.json
TGExplore May 18, 2020
d7bbb88
Update app.json
TGExplore May 18, 2020
4f85208
Update app.json
TGExplore May 18, 2020
0b32083
Update app.json
TGExplore May 18, 2020
518eb21
Update app.json
TGExplore May 18, 2020
9eca3a4
Update app.json
TGExplore May 18, 2020
c61eed6
Update app.json
TGExplore May 18, 2020
c1d3b78
Update app.json
TGExplore May 18, 2020
9b11f5a
Update app.json
TGExplore May 18, 2020
5b3b6d1
Delete app.json
TGExplore May 18, 2020
8acf78e
Added Deploy To Heroku
TGExplore May 18, 2020
b8e3ce6
Update app.json
TGExplore Jun 2, 2020
145af8d
Updated Button
TGExplore Jun 2, 2020
a24b333
Update README.md
TGExplore Jun 2, 2020
1ca357c
For Heroku
TGExplore Jun 2, 2020
b2b69ec
Support Group
TGExplore Jun 2, 2020
3a58de7
Added Support Group
TGExplore Jun 2, 2020
6f13c5d
Master
TGExplore Jun 2, 2020
f1d5862
typos
TGExplore Jun 2, 2020
9040f83
Typos
TGExplore Jun 2, 2020
af4ea49
Merge pull request #2 from TGExplore/For-Heroku
TGExplore Jun 2, 2020
7f65044
Added YouTube video link
TGExplore Jun 2, 2020
1f84a7c
Update README.md
TGExplore Jul 10, 2020
977c742
Create manual_screenshot.py
TGExplore Jul 10, 2020
769da4a
Update trim_2.py
TGExplore Jul 10, 2020
6ee0848
Update trim_1.py
TGExplore Jul 10, 2020
1f3fb10
Update utils.py
TGExplore Jul 10, 2020
857af09
Update database.py
TGExplore Jul 10, 2020
c5f8c7c
Create broadcast.py
TGExplore Jul 10, 2020
435c2f5
Update requirements.txt
TGExplore Jul 10, 2020
fc04ae7
Update screenshotbot.py
TGExplore Jul 10, 2020
dcd65ec
Added Reply "If document not a video type"
TGExplore Jul 10, 2020
7467c35
Merge pull request #7 from TGExplore/Manual_Screenshot
TGExplore Jul 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
# [Screenshotit_bot](https://tx.me/screenshotit_bot)
# [ScreenShot Bot](https://tx.me/ScreenShotTGBot)
> Telegram Bot For Screenshot Generation.

## Description

An attempt to implement the screenshot generation of telegram files without downloading the entire file. Live version can be found here [@screenshotit_bot](https://tx.me/screenshotit_bot "Screenshot Generator Bot").
An attempt to implement the screenshot generation of telegram files without downloading the entire file. Live version can be found here [@ScreenShot Bot](https://tx.me/ScreenShotTGBot).
> Screenshot Generation with Custom Watermark---Sample Video Generation---Trim video.

## Installation Guide

### You can also tap the Deploy To Heroku button below to deploy straight to Heroku!

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://www.heroku.com/deploy?template=https://github.com/TGExplore/Screenshot-Bot)

### Watch our YouTube video for more details - [Telegram Screenshot | Trim | Sample Video Generator Bot](https://youtu.be/Fsc-ZUvdO20)

### Prerequisites
* FFmpeg.
* Python3 (3.6 or higher).
Expand Down Expand Up @@ -41,7 +48,7 @@ Properly setup the environment variables or populate `config.py` with the values
* `LOG_CHANNEL`(required) - Log channel's id.
* `DATABASE_URL`(required) - Mongodb database URI.
* `AUTH_USERS`(required) - Authorised user(s) id separated by space.
* `HOST`(required) - Public URL of streaming service ([Source](https://github.com/tulir/tgfilestream "TgFileStream")).
* `HOST`(required) - Public URL of streaming service ([Source](https://github.com/TGExplore/musical-waddle)).
* `MAX_PROCESSES_PER_USER`(optional) - Number of parallel processes each user can have, defaults to 2.
* `MAX_TRIM_DURATION`(optional) - Maximum allowed seconds for trimming. Defaults to 600.
* `TRACK_CHANNEL`(optional) - User activity tracking channel's id. Only needed if you want to track and block any user. Disabled by default.
Expand All @@ -64,9 +71,11 @@ Now go to your bot and do a `/start`.
* `/ban_user` - Admin/Auth users only command. Command to ban any user. Usage: `/ban_user user_id ban_duration ban_reason`. `user_id` - telegram id of the user, `ban_duration` - ban duration in days, `ban_reason` - reason for ban. All 3 parameters are required.
* `/unban_user` - Admin/Auth users only command. Command to ban any banned user. Usage: `/unban_user user_id`. `user_id` - telegram id of the user. The parameter is required.
* `/banned_users` - Admin/Auth users only command. Command to view all banned users. Usage: `/banned_users`. This takes no parameters.
* `/broadcast` - Admin/Auth user only command. Command to broadcast some message to all users. Usage: reply `/broadcast` to the message you want to broadcast.

### Functions
* `Screenshot Generation` - Generates screenshots from telegram video files or streaming links. Number of screenshots range from 2-10.
* `Manual Screenshot` - Generates screenshots of specific time. Number of screenshots range from 1-10.
* `Sample Video Generation` - Generates sample video from telegram video files or streaming links. Video duration range from 30s to 150s. Configurable in `/settings`.
* `Video Trimming` - Trims any telegram video files or streaming links. Video duration depends on the environment. By default upto 10 mins (600s).

Expand All @@ -84,9 +93,12 @@ In bot settings.
Contributions are welcome.

## Contact
You can contact me [@odysseusmax](https://tx.me/odysseusmax).
You can contact me [@InFoTelGroup](https://tx.me/InFoTelGroup).

## Thanks

Thanks to [@odysseusmax](https://tx.me/odysseusmax) for his [animated-lamp](https://github.com/odysseusmax/animated-lamp) Bot.

Thanks to [Dan](https://github.com/delivrance "Dan") for his [Pyrogram](https://github.com/pyrogram/pyrogram "Pyrogram") library.

Thanks to [Tulir Asokan](https://github.com/tulir "Tulir Asokan") for his [TgFileStream](https://github.com/tulir/tgfilestream "TgFileStream") Bot.
Expand Down
73 changes: 73 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "Screenshot Bot",
"description": "Screenshot /Sample Video / Trim Video files",
"logo": "https://telegra.ph/file/a3a32dcceb8201635c1f0.jpg",
"keywords": [
"telegram",
"Screenshot",
"Sample video",
"Trim Video",
"without downloading the entire file"
],
"repository": "https://github.com/TGExplore/Screenshot-Bot",
"env": {
"API_ID": {
"description": "Your Telegram API ID. Get this value from my.telegram.org!",
"value": ""
},
"API_HASH": {
"description": " Your Telegram API HASH. Get this value from my.telegram.org!",
"value": ""
},
"BOT_TOKEN": {
"description": "Your Bot token, as a string.",
"value": ""
},
"SESSION_NAME": {
"description": "Name you want to call your bot's session, Eg: bot username.",
"value": ""
},
"LOG_CHANNEL": {
"description": "Log channel's id.",
"value": ""
},
"DATABASE_URL": {
"description": "Mongodb database URI from https://cloud.mongodb.com/",
"value": ""
},
"AUTH_USERS": {
"description": "Authorised user(s) id separated by space.",
"value": ""
},
"HOST": {
"description": "Public URL of streaming service (https://github.com/TGExplore/musical-waddle)",
"value": ""
},
"MAX_PROCESSES_PER_USER": {
"description": "Number of parallel processes each user can have, defaults to 2.",
"value": "2",
"required": false
},
"MAX_TRIM_DURATION": {
"description": "Maximum allowed seconds for trimming. Defaults to 600.",
"value": "600",
"required": false
},
"TRACK_CHANNEL": {
"description": "User activity tracking channel's id. Only needed if you want to track and block any user. Disabled by default.",
"required": false
},
"SLOW_SPEED_DELAY": {
"description": "Delay required between each request. Defaults to 15s.",
"value": "15",
"required": false
}
},
"addons": [
],
"buildpacks": [{
"url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest"
}, {
"url": "heroku/python"
}]
}
8 changes: 8 additions & 0 deletions bot/database/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,12 @@ async def get_ban_status(self, id):
async def get_all_banned_users(self):
banned_users = self.col.find({'ban_status.is_banned': True})
return banned_users


async def get_all_users(self):
all_users = self.col.find({})
return all_users


async def delete_user(self, user_id):
await self.col.delete_many({'id': int(user_id)})
161 changes: 161 additions & 0 deletions bot/plugins/broadcast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import traceback
import datetime
import asyncio
import string
import random
import time

from pyrogram import Filters, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.errors import FloodWait, InputUserDeactivated, UserIsBlocked, PeerIdInvalid
import aiofiles
import aiofiles.os

from bot.config import Config
from bot.screenshotbot import ScreenShotBot


async def send_msg(user_id, message):
try:
await message.forward(chat_id=user_id, as_copy=True)
return 200, None
except FloodWait as e:
await asyncio.sleep(e.x)
return send_msg(user_id, message)
except InputUserDeactivated:
return 400, f"{user_id} : deactivated\n"
except UserIsBlocked:
return 400, f"{user_id} : blocked the bot\n"
except PeerIdInvalid:
return 400, f"{user_id} : user id invalid\n"
except Exception as e:
return 500, f"{user_id} : {traceback.format_exc()}\n"



@ScreenShotBot.on_message(Filters.private & Filters.command("broadcast") & Filters.user(Config.AUTH_USERS) & Filters.reply)
async def broadcast_(c, m):
all_users = await c.db.get_all_users()

broadcast_msg = m.reply_to_message

while True:
broadcast_id = ''.join([random.choice(string.ascii_letters) for i in range(3)])
if not c.broadcast_ids.get(broadcast_id):
break

out = await m.reply_text(
text = f"Broadcast initiated! You will be notified with log file when all the users are notified.",
reply_markup = InlineKeyboardMarkup(
[
[
InlineKeyboardButton("Cancel Broadcast", callback_data=f"cncl_bdct+{broadcast_id}"),
InlineKeyboardButton("View broadcast status", callback_data=f"sts_bdct+{broadcast_id}")
]
]
)
)
start_time = time.time()
total_users = await c.db.total_users_count()
done = 0
failed = 0
success = 0

c.broadcast_ids[broadcast_id] = dict(
total = total_users,
current = done,
failed = failed,
success = success
)

async with aiofiles.open('broadcast.txt', 'w') as broadcast_log_file:
async for user in all_users:

sts, msg = await send_msg(
user_id = int(user['id']),
message = broadcast_msg
)
if msg is not None:
await broadcast_log_file.write(msg)

if sts == 200:
success += 1
else:
failed += 1

if sts == 400:
await c.db.delete_user(user['id'])

done += 1
if c.broadcast_ids.get(broadcast_id) is None:
break
else:
c.broadcast_ids[broadcast_id].update(
dict(
current = done,
failed = failed,
success = success
)
)
if c.broadcast_ids.get(broadcast_id):
c.broadcast_ids.pop(broadcast_id)
completed_in = datetime.timedelta(seconds=int(time.time()-start_time))

await asyncio.sleep(3)

await out.delete()

if failed == 0:
await m.reply_text(
text=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.",
quote=True
)
else:
await m.reply_document(
document='broadcast.txt',
caption=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.",
quote=True
)

await aiofiles.os.remove('broadcast.txt')

@ScreenShotBot.on_callback_query(Filters.create(lambda _, query: query.data.startswith('sts_bdct'))
& Filters.user(Config.AUTH_USERS))
async def sts_broadcast_(c, cb):

_, broadcast_id = cb.data.split('+')

if not c.broadcast_ids.get(broadcast_id):
await cb.answer(
text=f"No active broadcast with id {broadcast_id}",
show_alert=True
)
return

sts_txt = ''
for key, value in c.broadcast_ids[broadcast_id].items():
sts_txt += f'{key} = {value}\n'

await cb.answer(
text=f"Broadcast Status for {broadcast_id}\n\n{sts_txt}",
show_alert=True
)

@ScreenShotBot.on_callback_query(Filters.create(lambda _, query: query.data.startswith('cncl_bdct'))
& Filters.user(Config.AUTH_USERS))
async def cncl_broadcast_(c, cb):

_, broadcast_id = cb.data.split('+')

if not c.broadcast_ids.get(broadcast_id):
await cb.answer(
text=f"No active broadcast with id {broadcast_id}",
show_alert=True
)
return

c.broadcast_ids.pop(broadcast_id)

await cb.answer(
text="Broadcast will be canceled soon.",
show_alert=True
)
18 changes: 18 additions & 0 deletions bot/plugins/manual_screenshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import asyncio

from pyrogram import Filters, ForceReply

from ..screenshotbot import ScreenShotBot
from ..config import Config


@ScreenShotBot.on_callback_query(Filters.create(lambda _, query: query.data.startswith('mscht')))
async def _(c, m):
dur = m.message.text.markdown.split('\n')[-1]
await m.message.delete(True)
await c.send_message(
m.from_user.id,
f'#manual_screenshot\n\n{dur}\n\nNow send your list of seconds separated by `,`(comma).\nEg: `0,10,40,60,120`.\nThis will generate screenshots at 0, 10, 40, 60, and 120 seconds. \n\n1. The list can have a maximum of 10 valid positions.\n2. The position has to be greater than or equal to 0, or less than the video length in order to be valid.',
reply_to_message_id=m.message.reply_to_message.message_id,
reply_markup=ForceReply()
)
4 changes: 4 additions & 0 deletions bot/plugins/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ async def _(c, m):
)
return

if m.document:
if "video" not in m.document.mime_type:
await m.reply_text(f"**😟 Sorry! Only support Media Files.**\n**Your File type :** `{m.document.mime_type}.`", quote=True)

if not is_valid_file(m):
return

Expand Down
7 changes: 4 additions & 3 deletions bot/plugins/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ async def start(c, m):
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton('Source 😒', url='https://github.com/odysseusmax/animated-lamp'),
InlineKeyboardButton('Project Channel', url='https://t.me/odbots')
InlineKeyboardButton('📌 Support Group', url='https://t.me/InFoTelGroup'),
InlineKeyboardButton('🔖 Projects Channel', url='https://t.me/TGBotsZ')
],
[
InlineKeyboardButton('My Father', url='https://t.me/odysseusmax')
InlineKeyboardButton('💡 Source Code', url='https://github.com/TGExplore/Screenshot-Bot'),
InlineKeyboardButton('👨 Master', url='https://t.me/odbots')
]
]
)
Expand Down
3 changes: 2 additions & 1 deletion bot/plugins/trim_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

@ScreenShotBot.on_callback_query(Filters.create(lambda _, query: query.data.startswith('trim')))
async def _(c, m):
dur = m.message.text.markdown.split('\n')[-1]
await m.message.delete(True)
await c.send_message(
m.from_user.id,
f'Now send your start and end seconds in the given format and should be upto {Config.MAX_TRIM_DURATION}s. \n**start:end**\n\nEg: `400:500` ==> This trims video from 400s to 500s',
f'#trim_video\n\n{dur}\n\nNow send your start and end seconds in the given format and should be upto {Config.MAX_TRIM_DURATION}s. \n**start:end**\n\nEg: `400:500` ==> This trims video from 400s to 500s',
reply_to_message_id=m.message.reply_to_message.message_id,
reply_markup=ForceReply()
)
8 changes: 5 additions & 3 deletions bot/plugins/trim_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
from pyrogram import Filters, ForceReply

from ..config import Config
from ..utils import trim_fn
from ..utils import trim_fn, manual_screenshot_fn
from ..screenshotbot import ScreenShotBot


@ScreenShotBot.on_message(Filters.private & Filters.reply)
async def _(c, m):

if not await c.db.is_user_exist(m.chat.id):
await c.db.add_user(m.chat.id)
await c.send_message(
Expand All @@ -25,4 +24,7 @@ async def _(c, m):
print('not ForceReply')
return

asyncio.create_task(trim_fn(c, m))
if m.reply_to_message.text.startswith('#trim_video'):
asyncio.create_task(trim_fn(c, m))
else:
asyncio.create_task(manual_screenshot_fn(c, m))
1 change: 1 addition & 0 deletions bot/screenshotbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ def __init__(self):
self.db = Database(Config.DATABASE_URL, Config.SESSION_NAME)
self.CURRENT_PROCESSES = {}
self.CHAT_FLOOD = {}
self.broadcast_ids = {}
Loading