Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
42f95b6e9d |
169
.gitignore
vendored
|
@ -1,169 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# UV
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
#uv.lock
|
|
||||||
|
|
||||||
# poetry
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
||||||
#poetry.lock
|
|
||||||
|
|
||||||
# pdm
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
||||||
#pdm.lock
|
|
||||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
||||||
# in version control.
|
|
||||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
||||||
.pdm.toml
|
|
||||||
.pdm-python
|
|
||||||
.pdm-build/
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
BIN
architecture.png
Before Width: | Height: | Size: 35 KiB |
BIN
bot/.DS_Store
vendored
Normal file
21
bot/Dockerfile
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
FROM python:3.10.7
|
||||||
|
|
||||||
|
# python envs
|
||||||
|
ENV PYTHONFAULTHANDLER=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONHASHSEED=random \
|
||||||
|
PIP_NO_CACHE_DIR=off \
|
||||||
|
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||||
|
PIP_DEFAULT_TIMEOUT=100
|
||||||
|
|
||||||
|
# python dependencies
|
||||||
|
COPY ./requirements.txt /
|
||||||
|
RUN pip install -r /requirements.txt
|
||||||
|
|
||||||
|
COPY ./scripts/start.sh ./scripts/gunicorn.sh /
|
||||||
|
|
||||||
|
RUN chmod +x /start.sh
|
||||||
|
RUN chmod +x /gunicorn.sh
|
||||||
|
|
||||||
|
|
||||||
|
WORKDIR /app
|
BIN
bot/__pycache__/create_bot.cpython-312.pyc
Normal file
BIN
bot/__pycache__/memcached_def.cpython-312.pyc
Normal file
BIN
bot/__pycache__/messages.cpython-312.pyc
Normal file
BIN
bot/__pycache__/req.cpython-312.pyc
Normal file
BIN
bot/__pycache__/wrapper.cpython-312.pyc
Normal file
BIN
bot/avatar.jpg
Normal file
After Width: | Height: | Size: 7.7 KiB |
64
bot/back_send.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import json
|
||||||
|
import io
|
||||||
|
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from create_bot import bot, important_message, url
|
||||||
|
from aiogram import types
|
||||||
|
from create_bot import request_url, api_token
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo, ReplyKeyboardRemove
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
def get_answer_keyboard():
|
||||||
|
keyboard = InlineKeyboardMarkup()
|
||||||
|
button1 = InlineKeyboardButton('Главное меню', callback_data='main_menu_text')
|
||||||
|
keyboard.add(button1)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
|
class AlertSchema(BaseModel):
|
||||||
|
tg_id: int
|
||||||
|
message: str
|
||||||
|
|
||||||
|
class DispatchSchema(BaseModel):
|
||||||
|
tg_id: int
|
||||||
|
text: str
|
||||||
|
attachment_path: str
|
||||||
|
button_name: str | None = None
|
||||||
|
button_url: str | None = None
|
||||||
|
web_app_button_name: str | None = None
|
||||||
|
|
||||||
|
send_message_router = APIRouter(
|
||||||
|
prefix=''
|
||||||
|
)
|
||||||
|
|
||||||
|
@send_message_router.post('/dispatch/')
|
||||||
|
async def handle_dispatch(data: DispatchSchema, background_tasks: BackgroundTasks):
|
||||||
|
try:
|
||||||
|
media = io.BytesIO(requests.get(data.attachment_path).content)
|
||||||
|
media.seek(0)
|
||||||
|
attachment = types.InputFile(media)
|
||||||
|
keyboard = InlineKeyboardMarkup()
|
||||||
|
if data.button_name and data.button_url:
|
||||||
|
button1 = InlineKeyboardButton(f'{data.button_name}', url=f"{data.button_url}")
|
||||||
|
keyboard.add(button1)
|
||||||
|
if data.web_app_button_name:
|
||||||
|
button2 = InlineKeyboardButton(f'{data.web_app_button_name}', web_app=WebAppInfo(url=f"{url}"))
|
||||||
|
keyboard.add(button2)
|
||||||
|
|
||||||
|
if not data.button_name and not data.web_app_button_name:
|
||||||
|
await bot.send_photo(data.tg_id, attachment, caption=data.text, parse_mode=types.ParseMode.MARKDOWN, reply_markup=ReplyKeyboardRemove())
|
||||||
|
else:
|
||||||
|
await bot.send_photo(data.tg_id, attachment, caption=data.text, reply_markup=keyboard, parse_mode=types.ParseMode.MARKDOWN)
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
return {'ok': True}
|
||||||
|
|
||||||
|
@send_message_router.post('/alert/')
|
||||||
|
async def handle_alert(data: AlertSchema):
|
||||||
|
try:
|
||||||
|
await bot.send_message(chat_id=data.tg_id, text=data.message, parse_mode=types.ParseMode.MARKDOWN) #, reply_markup=get_answer_keyboard())
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
return {'ok': True}
|
27
bot/config.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import logging
|
||||||
|
from aiogram import Bot, types
|
||||||
|
from aiogram import Dispatcher
|
||||||
|
from create_bot import bot, token, WEBHOOK_URL
|
||||||
|
from handlers.register_handlers import register_all_handlers
|
||||||
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
dp = Dispatcher(bot, storage=MemoryStorage())
|
||||||
|
|
||||||
|
logger.add("logs.log", format = "{time} | {module} : {function} | {level} | {message}", level = "INFO", rotation = "1 week", compression = "zip")#, serialize = True)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_startup():
|
||||||
|
webhook_info = await bot.get_webhook_info()
|
||||||
|
if webhook_info.url != WEBHOOK_URL:
|
||||||
|
await bot.set_webhook(
|
||||||
|
url=WEBHOOK_URL,
|
||||||
|
drop_pending_updates=True
|
||||||
|
)
|
||||||
|
|
||||||
|
register_all_handlers(dp)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_shutdown():
|
||||||
|
await bot.session.close()
|
||||||
|
await bot.delete_webhook()
|
24
bot/create_bot.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from aiogram import Bot
|
||||||
|
import os
|
||||||
|
|
||||||
|
token = os.getenv('TG_TOKEN', '7748003961:AAEIXu8NFICPabNaQP5JQ3AcY79nZdUbKdI')
|
||||||
|
api_token = os.getenv('API_TOKEN', 'b43fa8ccea5b6dd5e889a8ad3890ce14ce36a8bc') # TODO: remove
|
||||||
|
backend_url = os.getenv('BACKEND_URL', 'http://backend:8000')
|
||||||
|
request_url = f'{backend_url}/api'
|
||||||
|
url = os.getenv('URL', 'https://google.com')
|
||||||
|
bot_name = os.getenv('BOT_NAME', 'https://t.me/danyadjan_test_bot')
|
||||||
|
|
||||||
|
bucket_name = 'brawny-basket'
|
||||||
|
username = 'e80165bc-8d55-42a3-a96b-f62314446f87'
|
||||||
|
password = '0d8e160fc5625ff0f176a0f70a22e336e8fb21a9841a5d20223714b7cee19341'
|
||||||
|
endpoint_url = 'https://s3.aeza.cloud/brawny-basket'
|
||||||
|
|
||||||
|
WEBHOOK_HOST = f'{url}/bot'
|
||||||
|
WEBHOOK_PATH = 'wh'
|
||||||
|
WEBHOOK_URL = f'{WEBHOOK_HOST}/{WEBHOOK_PATH}/{token}'
|
||||||
|
|
||||||
|
bot = Bot(token=token)
|
||||||
|
|
||||||
|
important_message = {}
|
||||||
|
|
||||||
|
event_number = {}
|
1
bot/handlers/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from cgitb import handler
|
BIN
bot/handlers/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
bot/handlers/__pycache__/instruction.cpython-312.pyc
Normal file
BIN
bot/handlers/__pycache__/register_handlers.cpython-312.pyc
Normal file
BIN
bot/handlers/__pycache__/start_handler.cpython-312.pyc
Normal file
103
bot/handlers/instruction.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
from aiogram import types
|
||||||
|
from aiogram import Bot, Dispatcher
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
from create_bot import bot, important_message, event_number
|
||||||
|
from memcached_def import add_rec, get_rec
|
||||||
|
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton, FSInputFile
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
def get_event_keyboard(number):
|
||||||
|
kb = []
|
||||||
|
dlina = 5
|
||||||
|
button1 = InlineKeyboardButton(text='←', callback_data="num_decr")
|
||||||
|
button2 = InlineKeyboardButton(text=f'{number + 1}/{dlina}', callback_data="element")
|
||||||
|
button3 = InlineKeyboardButton(text='→', callback_data="num_incr")
|
||||||
|
button4 = InlineKeyboardButton(text='Главное меню', callback_data='main_menu_delete')
|
||||||
|
if int(number + 1) == 1:
|
||||||
|
kb = [[button2, button3]]
|
||||||
|
elif int(number + 1) == dlina:
|
||||||
|
kb = [[button1, button2]]
|
||||||
|
else:
|
||||||
|
kb = [[button1, button2, button3]]
|
||||||
|
kb.append([button4])
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=kb)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
|
def read_data_from_file(file_path):
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
data_lines = file.readlines()
|
||||||
|
|
||||||
|
data_dict = {}
|
||||||
|
for line in data_lines:
|
||||||
|
key, value = line.strip().split(": ")
|
||||||
|
data_dict[key.strip()] = value.strip()
|
||||||
|
|
||||||
|
return data_dict
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("File not found.")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print("An error occurred:", e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_file(file_path, data_dict):
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
existing_data = file.readlines()
|
||||||
|
|
||||||
|
with open(file_path, 'w') as file:
|
||||||
|
for line in existing_data:
|
||||||
|
key = line.split(":")[0].strip()
|
||||||
|
if key in data_dict:
|
||||||
|
file.write(f"{key}: {data_dict[key]}\n")
|
||||||
|
else:
|
||||||
|
file.write(line)
|
||||||
|
|
||||||
|
for key, value in data_dict.items():
|
||||||
|
if key not in existing_data:
|
||||||
|
file.write(f"{key}: {value}\n")
|
||||||
|
|
||||||
|
# print("File updated successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
print("An error occurred:", e)
|
||||||
|
|
||||||
|
# Example usage:
|
||||||
|
file_path = "data.txt"
|
||||||
|
|
||||||
|
|
||||||
|
ins_list = ['1.png',
|
||||||
|
'2.png',
|
||||||
|
'3.png',
|
||||||
|
'4.png',
|
||||||
|
'5.png']
|
||||||
|
|
||||||
|
async def instruction_message(call: types.CallbackQuery):
|
||||||
|
logger.info(f"{call.from_user.id} - @{call.from_user.username} : инструкция")
|
||||||
|
|
||||||
|
add_rec(call.from_user.id, 0)
|
||||||
|
|
||||||
|
await call.message.delete()
|
||||||
|
await bot.send_photo(call.from_user.id, FSInputFile('pictures/1.png', 'rb'), reply_markup=get_event_keyboard(0))
|
||||||
|
|
||||||
|
async def update_instruction(message: types.Message, new_value: int):
|
||||||
|
photo = FSInputFile(f'pictures/{new_value + 1}.png')
|
||||||
|
await message.edit_media(types.InputMediaPhoto(media=photo), reply_markup=get_event_keyboard(new_value))
|
||||||
|
|
||||||
|
async def callbacks_instruction(callback: types.CallbackQuery):
|
||||||
|
user_value = int(get_rec(callback.from_user.id))
|
||||||
|
action = callback.data.split("_")[1]
|
||||||
|
|
||||||
|
if action == "incr":
|
||||||
|
if len(ins_list) > user_value + 1:
|
||||||
|
add_rec(callback.from_user.id, user_value + 1)
|
||||||
|
await update_instruction(callback.message, user_value + 1)
|
||||||
|
elif action == "decr":
|
||||||
|
if user_value - 1 >= 0:
|
||||||
|
add_rec(callback.from_user.id, user_value - 1)
|
||||||
|
await update_instruction(callback.message, user_value - 1)
|
||||||
|
#print("-1")
|
||||||
|
|
||||||
|
await callback.answer()
|
27
bot/handlers/register_handlers.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from aiogram import Router
|
||||||
|
from aiogram.filters import Command
|
||||||
|
from aiogram import F
|
||||||
|
|
||||||
|
from handlers.start_handler import (command_start,
|
||||||
|
get_main_menu_answer,
|
||||||
|
get_main_menu_after_picture)
|
||||||
|
|
||||||
|
from handlers.instruction import (instruction_message,
|
||||||
|
callbacks_instruction)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def register_all_handlers(router: Router):
|
||||||
|
handle_register_start_message(router)
|
||||||
|
handle_instruction_message(router)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_register_start_message(router: Router):
|
||||||
|
router.message.register(command_start, Command(commands=["start"]))
|
||||||
|
router.callback_query.register(get_main_menu_after_picture, F.data.startswith('main_menu_delete'))
|
||||||
|
router.callback_query.register(get_main_menu_answer, F.data.startswith('main_menu'))
|
||||||
|
router.callback_query.register(instruction_message, F.data.startswith("instruction_inline"))
|
||||||
|
|
||||||
|
def handle_instruction_message(router: Router):
|
||||||
|
router.callback_query.register(callbacks_instruction, F.data.startswith("num_"))
|
||||||
|
|
73
bot/handlers/start_handler.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import asyncio
|
||||||
|
from aiogram import Bot, Dispatcher, types
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import shutil
|
||||||
|
import random
|
||||||
|
from create_bot import bot, request_url, important_message, url, token, bucket_name, username, password, endpoint_url
|
||||||
|
from req import check_register
|
||||||
|
import urllib.request
|
||||||
|
from messages import get_main_menu_message
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
|
from aiogram.enums import ParseMode
|
||||||
|
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
from botocore.config import Config
|
||||||
|
|
||||||
|
def get_answer_keyboard():
|
||||||
|
button1 = InlineKeyboardButton(text='Главное меню', callback_data='main_menu')
|
||||||
|
kb = [[button1]]
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=kb)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
|
def get_main_keyboard_inline(is_admin=False, ref_code=None):
|
||||||
|
button1 = InlineKeyboardButton(text='👾 Кликер', web_app=WebAppInfo(url=f'{url}?referred_by={ref_code}'))
|
||||||
|
button2 = InlineKeyboardButton(text='ℹ️ Инструкция', callback_data='instruction_inline')
|
||||||
|
button3 = InlineKeyboardButton(text='🔥 Кто мы?', web_app=WebAppInfo(url='https://test.com'))
|
||||||
|
button4 = InlineKeyboardButton(text='ТЕСТ', callback_data='test_message')
|
||||||
|
kb = [[button1], [button2], [button3]]
|
||||||
|
if is_admin:
|
||||||
|
kb.append([button4])
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=kb)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
|
def gen_ok_keyboard(number):
|
||||||
|
kb = [[InlineKeyboardButton(text='Подтвердить ✅', callback_data=f'approve_{number}'), InlineKeyboardButton(text='Изменить 🔄', callback_data=f'edit_{number}')]]
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=kb)
|
||||||
|
return keyboard
|
||||||
|
|
||||||
|
async def get_main_menu_answer(call: types.CallbackQuery):
|
||||||
|
logger.info(f"{call.from_user.id} - @{call.from_user.username} : главное меню через инлайн кнопку")
|
||||||
|
await call.message.edit_text(get_main_menu_message(), reply_markup=get_main_keyboard_inline(), parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
async def get_main_menu_after_picture(call: types.CallbackQuery):
|
||||||
|
logger.info(f"{call.from_user.id} - @{call.from_user.username} : главное меню через инлайн кнопку после картинки")
|
||||||
|
await call.message.delete()
|
||||||
|
await bot.send_message(call.from_user.id, get_main_menu_message(), reply_markup=get_main_keyboard_inline(), parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
async def command_start(message : types.Message):
|
||||||
|
ref_code = ''
|
||||||
|
if message.text[7:].startswith('user_'):
|
||||||
|
ref_code = message.text[12:]
|
||||||
|
if not check_register(message.from_user.id):
|
||||||
|
|
||||||
|
logger.info(f"{message.from_user.id} - @{message.from_user.username} : команда /start и не зарегистрирован")
|
||||||
|
await bot.send_message(message.from_user.id, '👋', reply_markup=ReplyKeyboardRemove())
|
||||||
|
# await asyncio.sleep(3)
|
||||||
|
await bot.send_message(message.from_user.id, '👑 Я - KYC Кликер бот! Зарабатывай баллы кликами, поднимайся в рейтинге и получай бонусы. Развивайся быстрее с нашей специальной системой для новичков!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
# await asyncio.sleep(3)
|
||||||
|
await bot.send_message(message.from_user.id, '🎁 Используй баллы в аукционе за ценные призы! Победителей много, приглашаем в увлекательную битву кликов!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
# await asyncio.sleep(3)
|
||||||
|
await bot.send_message(message.from_user.id, '👯 Участвуй в реферальной программе, чтобы получать % с кликов друзей и зарабатывать больше баллов для аукциона. ', reply_markup=ReplyKeyboardRemove())
|
||||||
|
# await asyncio.sleep(3)
|
||||||
|
await bot.send_message(message.from_user.id, '🍀 Удачи в битве!', reply_markup=ReplyKeyboardRemove())
|
||||||
|
# await asyncio.sleep(3)
|
||||||
|
else:
|
||||||
|
logger.info(f"{message.from_user.id} - @{message.from_user.username} : команда /start")
|
||||||
|
|
||||||
|
await bot.send_message(message.from_user.id, get_main_menu_message(), reply_markup=get_main_keyboard_inline(ref_code=ref_code), parse_mode=ParseMode.MARKDOWN)
|
21
bot/log.ini
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[loggers]
|
||||||
|
keys=root
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys=logfile
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys=logfileformatter
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level=INFO
|
||||||
|
handlers=logfile
|
||||||
|
|
||||||
|
[formatter_logfileformatter]
|
||||||
|
format=[%(asctime)s.%(msecs)03d] %(levelname)s [%(thread)d] - %(message)s
|
||||||
|
|
||||||
|
[handler_logfile]
|
||||||
|
class=handlers.RotatingFileHandler
|
||||||
|
level=INFO
|
||||||
|
args=('logfile.log','a')
|
||||||
|
formatter=logfileformatter
|
8
bot/logs.log
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
2024-10-27T23:59:11.744491+0300 | main : main | INFO | Starting bot
|
||||||
|
2024-10-27T23:59:12.269505+0300 | start_handler : command_start | INFO | 7080735869 - @danya_danya23 : команда /start и не зарегистрирован
|
||||||
|
2024-10-28T00:00:35.215099+0300 | start_handler : command_start | INFO | 193428034 - @s1lur : команда /start и не зарегистрирован
|
||||||
|
2024-10-28T00:00:35.270428+0300 | start_handler : command_start | INFO | 402449803 - @danyadjan : команда /start и не зарегистрирован
|
||||||
|
2024-10-28T00:01:50.305932+0300 | main : main | INFO | Starting bot
|
||||||
|
2024-10-28T00:01:50.735100+0300 | start_handler : command_start | INFO | 402449803 - @danyadjan : команда /start и не зарегистрирован
|
||||||
|
2024-10-28T00:02:04.042345+0300 | main : main | INFO | Starting bot
|
||||||
|
2024-10-28T00:02:06.132110+0300 | start_handler : command_start | INFO | 402449803 - @danyadjan : команда /start и не зарегистрирован
|
15
bot/main.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import asyncio
|
||||||
|
from loguru import logger
|
||||||
|
from wrapper import run_bot
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
logger.info("Starting bot")
|
||||||
|
asyncio.run(run_bot())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logger.info("Interrupted by user, shutting down")
|
||||||
|
return
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
52
bot/main_api.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import os
|
||||||
|
import secrets
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from create_bot import bot
|
||||||
|
from config import on_startup, on_shutdown
|
||||||
|
from routes import bot_api_router
|
||||||
|
from back_send import send_message_router
|
||||||
|
|
||||||
|
def init_app():
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title='Clicker Bot',
|
||||||
|
description='',
|
||||||
|
version='1',
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.on_event('startup')
|
||||||
|
async def startup():
|
||||||
|
await on_startup()
|
||||||
|
|
||||||
|
@app.on_event('shutdown')
|
||||||
|
async def shutdown():
|
||||||
|
await on_shutdown()
|
||||||
|
|
||||||
|
app.include_router(
|
||||||
|
bot_api_router,
|
||||||
|
prefix=''
|
||||||
|
)
|
||||||
|
|
||||||
|
app.include_router(
|
||||||
|
send_message_router,
|
||||||
|
prefix=''
|
||||||
|
)
|
||||||
|
|
||||||
|
origins = [
|
||||||
|
'bot:7313',
|
||||||
|
'localhost:7313',
|
||||||
|
]
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=['*'],
|
||||||
|
allow_headers=['*']
|
||||||
|
)
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = init_app()
|
11
bot/memcached_def.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import os
|
||||||
|
from pymemcache.client import base
|
||||||
|
|
||||||
|
client = base.Client((os.getenv('mem_host', 'localhost'), os.getenv('mem_port', 11211)))
|
||||||
|
|
||||||
|
def add_rec(key, value):
|
||||||
|
client.set(str(key), value)
|
||||||
|
|
||||||
|
def get_rec(key):
|
||||||
|
value = client.get(str(key))
|
||||||
|
return (int(value))
|
6
bot/messages.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
def get_main_menu_message():
|
||||||
|
msg = '🏠*ГЛАВНОЕ МЕНЮ*'
|
||||||
|
msg1 = '👾 *Кликер* - Играй и соревнуйся с другими участниками!'
|
||||||
|
msg2 = 'ℹ️ *Инструкция* - Читай, чтобы правильно использовать кликер!'
|
||||||
|
msg3 = '🔥 *Кто мы?* - Узнай больше о нас и нашей компании!'
|
||||||
|
return f'{msg}\n\n{msg1}\n{msg2}\n{msg3}'
|
BIN
bot/pictures/.DS_Store
vendored
Normal file
BIN
bot/pictures/1.png
Normal file
After Width: | Height: | Size: 614 KiB |
BIN
bot/pictures/2.png
Normal file
After Width: | Height: | Size: 666 KiB |
BIN
bot/pictures/3.png
Normal file
After Width: | Height: | Size: 561 KiB |
BIN
bot/pictures/4.png
Normal file
After Width: | Height: | Size: 642 KiB |
BIN
bot/pictures/5.png
Normal file
After Width: | Height: | Size: 571 KiB |
14
bot/req.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from loguru import logger
|
||||||
|
from create_bot import request_url, api_token
|
||||||
|
from operator import itemgetter
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
def check_register(tg_id):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_admin(tg_id):
|
||||||
|
return 0
|
17
bot/routes.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from aiogram import types, Dispatcher, Bot
|
||||||
|
from create_bot import bot, token
|
||||||
|
from config import dp
|
||||||
|
|
||||||
|
|
||||||
|
bot_api_router = APIRouter(
|
||||||
|
prefix=f'/bot/wh',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bot_api_router.post(f'/{token}', include_in_schema=False)
|
||||||
|
async def bot_webhook(update: dict):
|
||||||
|
telegram_update = types.Update(**update)
|
||||||
|
Dispatcher.set_current(dp)
|
||||||
|
Bot.set_current(bot)
|
||||||
|
await dp.process_update(telegram_update)
|
7
bot/scripts/gunicorn.sh
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
gunicorn main_api:app -w 17 -b 0.0.0.0:7313 -k uvicorn.workers.UvicornWorker --timeout 600
|
8
bot/scripts/start.sh
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
set -o xtrace
|
||||||
|
|
||||||
|
uvicorn main_api:app --host 0.0.0.0 --port 7313 --log-config /app/log.ini
|
23
bot/wrapper.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from handlers.register_handlers import register_all_handlers
|
||||||
|
from create_bot import bot
|
||||||
|
from aiogram import Bot, Dispatcher
|
||||||
|
from aiogram.fsm.storage.memory import MemoryStorage
|
||||||
|
from loguru import logger
|
||||||
|
from aiogram import Router
|
||||||
|
|
||||||
|
logger.add("logs.log", format = "{time} | {module} : {function} | {level} | {message}", level = "INFO", rotation = "1 week", compression = "zip")#, serialize = True)
|
||||||
|
|
||||||
|
async def run_bot():
|
||||||
|
storage = MemoryStorage()
|
||||||
|
dp = Dispatcher(storage=storage)
|
||||||
|
|
||||||
|
# Create a router to register handlers
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
# Register all handlers
|
||||||
|
register_all_handlers(router)
|
||||||
|
|
||||||
|
# Mount the router into the dispatcher
|
||||||
|
dp.include_router(router)
|
||||||
|
|
||||||
|
await dp.start_polling(bot)
|