mirror of
https://github.com/kurbezz/utility-bot.git
synced 2025-12-06 07:15:37 +01:00
Init
This commit is contained in:
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
50
.github/workflows/build_docker_image.yml
vendored
Normal file
50
.github/workflows/build_docker_image.yml
vendored
Normal 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
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
venv
|
||||
28
docker/build.dockerfile
Normal file
28
docker/build.dockerfile
Normal 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
1329
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
pyproject.toml
Normal file
20
pyproject.toml
Normal 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
10
src/config.py
Normal 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
100
src/main.py
Normal 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()
|
||||
Reference in New Issue
Block a user