Скачиваем проект с гитхаба. Запускаем проект в любой среде разработки для python (Например: PyCharm).
5 |
Среда разработки автоматически подгрузит необходимые библиотеки с файла requirements.
6 |
Заменяем Token от BotFather в файле main.py
7 |
8 |
Запускаем проект
9 |
Со второго аккаунта нажимаем /start и пишем слово "admin"
10 |
Выключаем проект и заполняем admin_id и config_id в файле main.py
11 |
Запускаем проект и с аккаунта пользователя нажимаем старт
12 |
13 |
Профит
14 |
15 |
Тестирование и графики
16 | Тесты проводились на серверах heroku с минимальными характеристиками инстансов. Так, что можно считать, что все тесты были выполнены в более менее равных условиях.
17 |
18 | Графики сделаны по выборкам из ~100 запросов. И представлены средние показатели выборки.
19 |
20 | В качестве базы данных на стороннем сервере использовался PostgreSQL на Amazon RDS с минимальными характеристиками.
21 |
22 |
23 | При одном миллионе пользователей время бэкапов становится проблемой.
24 |
25 |
26 |
27 | Размер бэкапа полностью зависит от вашей модели данных, в моем случае при одном миллионе пользователей получилось данных на 21 мегабайт.
28 |
29 |
30 |
31 |
Вывод
32 |
33 | Данный метод хранения данных имеет смысл для проектов до миллиона пользователей. То есть для прототипа или личного стартапа данный способ имеет право на жизнь.
34 |
35 | В итоге мы получили полностью автономного кликера, независящий от удаленных баз данных.
36 |
37 | Вот выше описанный проект, развернутый на heroku: @Clicker_fast_bot
38 |
39 | Так же я реализовал более сложный проект с данной идеологией: @Random_friend_bot
40 |
41 | Подобие чатвдвоем и чатрулет, но только в телеграмме.
42 |
43 | Спасибо за внимание!
44 |
45 |
46 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import io
3 | import logging
4 | import threading
5 | import time
6 |
7 | from aiogram import Bot, Dispatcher, executor, types
8 | from aiogram.types import InputMediaDocument, KeyboardButton, ReplyKeyboardMarkup
9 | from urllib.request import urlopen
10 | import json
11 | import sqlite3
12 | #--------------------Настройки бота-------------------------
13 |
14 | # Ваш токен от BotFather
15 | TOKEN = '1234567:your_token'
16 |
17 | # Логирование
18 | logging.basicConfig(level=logging.INFO)
19 |
20 | bot = Bot(token=TOKEN)
21 | dp = Dispatcher(bot)
22 |
23 | # Ваш айди аккаунта администратора и айди сообщения где хранится файл с данными
24 | admin_id=12345678
25 | config_id=123
26 |
27 | conn = sqlite3.connect(":memory:") # или :memory: чтобы сохранить в RAM
28 | cursor = conn.cursor()
29 |
30 |
31 | # #--------------------Получение данных-------------------------
32 | async def get_data():
33 | to = time.time()
34 | # Пересылаем сообщение в данными от админа к админу
35 | forward_data = await bot.forward_message(admin_id, admin_id, config_id)
36 |
37 | # Получаем путь к файлу, который переслали
38 | file_data = await bot.get_file(forward_data.document.file_id)
39 |
40 | # Получаем файл по url
41 | file_url_data = bot.get_file_url(file_data.file_path)
42 |
43 | # Считываем данные с файла
44 | json_file= urlopen(file_url_data).read()
45 | print('Время получения бекапа :=' + str(time.time() - to))
46 | # Переводим данные из json в словарь и возвращаем
47 | return json.loads(json_file)
48 |
49 |
50 | #--------------------Сохранение данных-------------------------
51 | async def save_data():
52 | to = time.time()
53 | sql = "SELECT * FROM users "
54 | cursor.execute(sql)
55 | data = cursor.fetchall() # or use fetchone()
56 | try:
57 | # Переводим словарь в строку
58 | str_data=json.dumps(data)
59 |
60 | # Обновляем наш файл с данными
61 | await bot.edit_message_media(InputMediaDocument(io.StringIO(str_data)), admin_id, config_id)
62 |
63 | except Exception as ex:
64 | print(ex)
65 | print('Время сохранения бекапа:='+str(time.time() - to))
66 |
67 | #--------------------Метод при нажатии start-------------------------
68 | @dp.message_handler(commands='start')
69 | async def start(message: types.Message):
70 | # Добавляем нового пользователя
71 | sql_select = "SELECT * FROM users where chatid={}".format(message.chat.id)
72 | sql_insert = "INSERT INTO users VALUES ({}, '{}', {},{})".format(message.chat.id,message.chat.first_name, 0, 0)
73 | try:
74 | cursor.execute(sql_select)
75 | data = cursor.fetchone()
76 | if data is None:
77 | cursor.execute(sql_insert)
78 | conn.commit()
79 | await save_data()
80 | except Exception:
81 | data = await get_data()
82 | cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)")
83 | cursor.executemany("INSERT INTO users VALUES (?,?,?,?)", data)
84 | conn.commit()
85 | cursor.execute(sql_select)
86 | data = cursor.fetchone()
87 | if data is None:
88 | cursor.execute(sql_insert)
89 | conn.commit()
90 | await save_data()
91 | # Создаем кнопки
92 | button = KeyboardButton('Клик')
93 | button2 = KeyboardButton('Рейтинг')
94 | # Добавляем
95 | kb = ReplyKeyboardMarkup(resize_keyboard=True).add(button).add(button2)
96 | # Отправляем сообщение с кнопкой
97 | await bot.send_message(message.chat.id,'Приветствую {}'.format(message.chat.first_name),reply_markup=kb)
98 |
99 |
100 |
101 | #--------------------Основная логика бота-------------------------
102 | @dp.message_handler()
103 | async def main_logic(message: types.Message):
104 |
105 | to=time.time()
106 | # Логика для администратора
107 | if message.text == 'admin':
108 | cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)")
109 | cursor.execute("INSERT INTO users VALUES (1234, 'eee', 1,0)")
110 | conn.commit()
111 | sql = "SELECT * FROM users "
112 | cursor.execute(sql)
113 | data = cursor.fetchall()
114 | str_data = json.dumps(data)
115 | await bot.send_document(message.chat.id, io.StringIO(str_data))
116 | await bot.send_message(message.chat.id, 'admin_id = {}'.format(message.chat.id))
117 | await bot.send_message(message.chat.id, 'config_id = {}'.format(message.message_id+1))
118 |
119 |
120 | # Логика для пользователя
121 | try:
122 | sql = "SELECT * FROM users where chatid={}".format(message.chat.id)
123 | cursor.execute(sql)
124 | data = cursor.fetchone() # or use fetchone()
125 | except Exception:
126 | data = await get_data()
127 | cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)")
128 | cursor.executemany("INSERT INTO users VALUES (?,?,?,?)", data)
129 | conn.commit()
130 | sql = "SELECT * FROM users where chatid={}".format(message.chat.id)
131 | cursor.execute(sql)
132 | data = cursor.fetchone() # or use fetchone()
133 |
134 |
135 | #При нажатии кнопки клик увеличиваем значение click на один и сохраняем
136 | if data is not None:
137 | if message.text == 'Клик':
138 | sql = "UPDATE users SET click = {} WHERE chatid = {}".format(data[2]+1,message.chat.id)
139 | cursor.execute(sql)
140 | conn.commit()
141 | await bot.send_message(message.chat.id, 'Кликов: {} 🏆'.format(data[2]+1))
142 |
143 |
144 |
145 | # При нажатии кнопки Рейтинг выводим пользователю топ 10
146 | if message.text == 'Рейтинг':
147 | sql = "SELECT * FROM users ORDER BY click DESC LIMIT 15"
148 | cursor.execute(sql)
149 | newlist = cursor.fetchall() # or use fetchone()
150 | sql_count = "SELECT COUNT(chatid) FROM users"
151 | cursor.execute(sql_count)
152 | count=cursor.fetchone()
153 | rating='Всего: {}\n'.format(count[0])
154 | i=1
155 | for user in newlist:
156 | rating=rating+str(i)+': '+user[1]+' - '+str(user[2])+'🏆\n'
157 | i+=1
158 | await bot.send_message(message.chat.id, rating)
159 |
160 |
161 |
162 | else:
163 | await bot.send_message(message.chat.id, 'Вы не зарегистрированы')
164 |
165 | print(time.time()-to)
166 |
167 |
168 |
169 | def timer_start():
170 | threading.Timer(30.0, timer_start).start()
171 | try:
172 | asyncio.run_coroutine_threadsafe(save_data(),bot.loop)
173 | except Exception as exc:
174 | pass
175 |
176 |
177 | #--------------------Запуск бота-------------------------
178 | if __name__ == '__main__':
179 | timer_start()
180 | executor.start_polling(dp, skip_updates=True)
181 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiogram
2 | aiohttp
3 |
--------------------------------------------------------------------------------
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.7.0
2 |
--------------------------------------------------------------------------------