This commit is contained in:
2024-08-10 03:28:13 +02:00
commit c725f18c96
8 changed files with 1545 additions and 0 deletions

7
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@@ -0,0 +1,50 @@
name: Build docker image
on:
push:
branches:
- 'main'
jobs:
Build-Docker-Image:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- id: repository_name
uses: ASzc/change-string-case-action@v6
with:
string: ${{ github.repository }}
-
name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v6
env:
IMAGE: ${{ steps.repository_name.outputs.lowercase }}
with:
push: true
platforms: linux/amd64
tags: ghcr.io/${{ env.IMAGE }}:latest
context: .
file: ./docker/build.dockerfile
# -
# name: Invoke deployment hook
# uses: joelwmale/webhook-action@master
# with:
# url: ${{ secrets.WEBHOOK_URL }}

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
venv

28
docker/build.dockerfile Normal file
View File

@@ -0,0 +1,28 @@
FROM python:3.12-slim AS build
ARG POETRY_EXPORT_EXTRA_ARGS=''
WORKDIR /opt/venv
RUN python -m venv /opt/venv && /opt/venv/bin/pip install --upgrade pip && /opt/venv/bin/pip install --no-cache-dir httpx poetry
COPY ./pyproject.toml ./poetry.lock ./
RUN --mount=type=ssh /opt/venv/bin/poetry export --without-hashes ${POETRY_EXPORT_EXTRA_ARGS} > requirements.txt \
&& /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim AS runtime
RUN apt update && apt install -y --no-install-recommends netcat-traditional wkhtmltopdf && apt clean
COPY ./src/ /app
ENV PATH="/opt/venv/bin:$PATH"
ENV VENV_PATH=/opt/venv
COPY --from=build /opt/venv /opt/venv
WORKDIR /app
EXPOSE 80
CMD ["python", "main.py"]

1329
poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

20
pyproject.toml Normal file
View File

@@ -0,0 +1,20 @@
[tool.poetry]
name = "utility-bot"
version = "0.1.0"
description = ""
authors = ["Bulat Kurbanov <kurbanovbul@gmail.com>"]
readme = "README.md"
packages = [{include = "utility_bot"}]
[tool.poetry.dependencies]
python = "^3.11"
tiktok-downloader = "^0.3.5"
aiogram = "^3.11.0"
aiohttp = "^3.10.2"
pydantic = "^2.8.2"
pydantic-settings = "^2.4.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

10
src/config.py Normal file
View File

@@ -0,0 +1,10 @@
from pydantic_settings import BaseSettings
class Config(BaseSettings):
BOT_TOKEN: str
BASE_WEBHOOK_URL: str
WEBHOOK_SECRET: str
config = Config() # type: ignore

100
src/main.py Normal file
View File

@@ -0,0 +1,100 @@
from io import BufferedWriter, BytesIO
import re
import asyncio
import sys
import logging
from aiohttp import web
from aiogram import Bot, Dispatcher, types
from aiogram.enums import ParseMode
from aiogram.client.default import DefaultBotProperties
from tiktok_downloader import VideoInfo, tikwm, ttdownloader, tikdown, mdown, snaptik, Tikmate
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from config import config
dp = Dispatcher()
WEB_SERVER_HOST = "0.0.0.0"
WEB_SERVER_PORT = 80
WEBHOOK_PATH = "/webhook"
WEBHOOK_SECRET = config.WEBHOOK_SECRET
BASE_WEBHOOK_URL = config.BASE_WEBHOOK_URL
def download(link: str):
download_funcs = [
VideoInfo.service,
tikwm,
ttdownloader,
tikdown,
mdown,
snaptik,
Tikmate
]
for download_func in download_funcs:
try:
d = download_func(link)
if d:
data = d[0].download()
if data is not None:
return data
except Exception:
pass
@dp.message()
async def message_handler(message: types.Message):
if message.text is None:
await message.answer('This is not a text message')
return
tiktok_link = re.search(r'https://vt.tiktok.com/.+/', message.text)
if tiktok_link is None:
await message.answer('This is not a TikTok link')
return
await message.answer('Downloading...')
loop = asyncio.get_event_loop()
data: BytesIO | BufferedWriter | None = await loop.run_in_executor(None, download, tiktok_link.group(0))
if data is None:
await message.answer('Failed to download the video')
return
filename = 'video.mp4'
if isinstance(data, bytes):
await message.answer_video(types.BufferedInputFile(data, filename=filename))
elif isinstance(data, BytesIO):
await message.answer_video(types.BufferedInputFile(data.getvalue(), filename=filename))
elif isinstance(data, BufferedWriter):
temp = BytesIO()
data.write(temp.getbuffer())
await message.answer_video(types.BufferedInputFile(temp.getvalue(), filename=filename))
def main() -> None:
bot = Bot(token=config.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
app = web.Application()
webhook_requests_handler = SimpleRequestHandler(
dispatcher=dp,
bot=bot,
secret_token=WEBHOOK_SECRET,
)
webhook_requests_handler.register(app, path=WEBHOOK_PATH)
setup_application(app, dp, bot=bot)
web.run_app(app, host=WEB_SERVER_HOST, port=WEB_SERVER_PORT)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
main()