├── Tasks └── .geetkeep ├── db.db ├── .gitignore ├── requirements.txt ├── keyboard.py ├── README.md ├── sqlite.py └── bot.py /Tasks/.geetkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /db.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarefWeb/OrderDelivery/HEAD/db.db -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__/ 3 | тз.docx 4 | train.py 5 | config.py 6 | creds.json -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests~=2.24.0 2 | httplib2~=0.18.1 3 | aiogram~=2.9.2 4 | oauth2client~=4.1.3 5 | google-api-python-client~=1.10.0 -------------------------------------------------------------------------------- /keyboard.py: -------------------------------------------------------------------------------- 1 | from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup 2 | 3 | work_type_kb = InlineKeyboardMarkup().row(InlineKeyboardButton('Телеграм бот', callback_data='work_type_bot'), 4 | InlineKeyboardButton('Сайт', callback_data='work_type_site')) 5 | 6 | order_answer_kb = InlineKeyboardMarkup().row(InlineKeyboardButton('Принять', callback_data='order_answer_accept'), 7 | InlineKeyboardButton('Отклонить', callback_data='order_answer_dismiss')) 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram bot 2 | Bot for receiving and processing orders in Telegram. 3 | ### How does the bot work? 4 | 1. The customer answers the bot's questions. 5 | 6 | 2. After the customer has answered all the questions the bot sends the processed version of the order to the telegram channel with administrators and asks to wait for the customer to answer. 7 | 8 | 3. The channel receives the order and under it two buttons "accept" and "reject". 9 | 10 | 4. A). When clicking on the "accept" button, all the order data is entered into the google table and the client receives a message that the order has been accepted. 11 | B). When clicking on the "reject" button, the order is deleted from the chat and the client receives a message about rejection. 12 | -------------------------------------------------------------------------------- /sqlite.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from config import Step 3 | 4 | 5 | class SQLighter: 6 | 7 | def __init__(self, db_name): 8 | self.con = sqlite3.connect(db_name) 9 | self.cursor = self.con.cursor() 10 | 11 | def add_client(self, chat_id, username): 12 | with self.con: 13 | self.cursor.execute("INSERT INTO `clients` (`chat_id`, `username`, `step`) VALUES (?, ?, ?)", 14 | (chat_id, username, Step.CHOOSE_WORK_TYPE.value)) 15 | 16 | def update_work_type(self, username, work_type): 17 | with self.con: 18 | self.cursor.execute("UPDATE `clients` SET `work_type`= ? WHERE `username` == ?", (work_type, username)) 19 | 20 | def update_budget(self, username, budget): 21 | with self.con: 22 | self.cursor.execute("UPDATE `clients` SET `budget`= ? WHERE `username` == ?", (budget, username)) 23 | 24 | def update_task_file(self, username, task_file): 25 | with self.con: 26 | self.cursor.execute("UPDATE `clients` SET `task_file` = ? WHERE `username` == ?", (task_file, username)) 27 | 28 | def update_step(self, username, step): 29 | with self.con: 30 | self.cursor.execute("UPDATE `clients` SET `step` = ? WHERE `username` == ?", (step, username)) 31 | 32 | def get_step(self, username): 33 | with self.con: 34 | return self.cursor.execute("SELECT `step` FROM `clients` WHERE `username` == ?", [username]).fetchone() 35 | 36 | def get_client_data(self, username): 37 | with self.con: 38 | return self.cursor.execute("SELECT * FROM `clients` WHERE `username` == ?", [username]).fetchone() 39 | 40 | def active_order(self, username): 41 | with self.con: 42 | self.cursor.execute("UPDATE `clients` SET `status` = TRUE WHERE `username` == ?", [username]) 43 | 44 | def get_order_status(self, username): 45 | with self.con: 46 | return self.cursor.execute("SELECT `status` FROM `clients` WHERE `username` == ?", [username]).fetchone() 47 | 48 | def reset_data(self, username): 49 | with self.con: 50 | self.cursor.execute("UPDATE `clients` SET `work_type` = NULL, `budget` = NULL, `task_file` = NULL, \ 51 | `status` = 0, `step` = ? WHERE `username` == ?", (Step.CHOOSE_WORK_TYPE.value, username)) 52 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | from aiogram import Bot, Dispatcher, executor, types 2 | from config import TOKEN, DB_NAME, Step, CHAT_OUTPUT, CREDENTIALS_FILE, SPREAD_SHEET_ID 3 | from keyboard import work_type_kb, order_answer_kb 4 | from sqlite import SQLighter 5 | from oauth2client.service_account import ServiceAccountCredentials 6 | import requests 7 | import os 8 | import random 9 | import httplib2 10 | import googleapiclient.discovery 11 | 12 | bot = Bot(TOKEN) 13 | dp = Dispatcher(bot) 14 | 15 | db = SQLighter(DB_NAME) 16 | 17 | 18 | async def add_client(message, chat_id, username): 19 | db.add_client(chat_id=chat_id, username=username) 20 | await message.answer('Здравствуйте. Какой вид услуг вас интересует?', reply_markup=work_type_kb) 21 | 22 | 23 | @dp.message_handler(commands=['start']) 24 | async def start(message: types.Message): 25 | if 'username' in message.from_user: 26 | chat_id = message.from_user.id 27 | username = '@' + message.from_user.username 28 | step_list = db.get_step(username) 29 | 30 | if step_list is None: 31 | await add_client(message=message, chat_id=chat_id, username=username) 32 | 33 | else: 34 | step = step_list[0] 35 | 36 | if step == Step.CHOOSE_WORK_TYPE.value: 37 | await message.answer('Сейчас вы находитесь на этапе выбора типа работы.\n' 38 | 'Так какой вид услуг вас интересует?', reply_markup=work_type_kb) 39 | 40 | elif step == Step.ENTER_BUDGET.value: 41 | await message.answer('Сейчас вы находитесь на этапе ввода бюджета проекта.\n' 42 | 'Если бюджет по нашему усмотрению, то введите "-".\n' 43 | 'Или сумму, на которуюв ы расчитываете? (в рублях)') 44 | 45 | elif step == Step.SEND_TASK_FILE.value: 46 | await message.answer('Сейчас вы находитесь на этапе отправки технического задания.\n' 47 | 'Отправьте файл, в котором будет описана цель работы и ваши предпочтения:') 48 | 49 | elif step == Step.COMPLETE.value: 50 | db.reset_data(username=username) 51 | await message.answer('Давно не виделись. Какой вид услуг вас интересует сейчас?', 52 | reply_markup=work_type_kb) 53 | 54 | else: 55 | await add_client(message=message, chat_id=chat_id, username=username) 56 | 57 | else: 58 | await message.answer('Извините, у вас отсутствует логин. Добавьте логин и возвращайтесь к нам!') 59 | 60 | 61 | @dp.message_handler(commands=['reset']) 62 | async def reset(message: types.Message): 63 | username = '@' + message.from_user.username 64 | 65 | db.reset_data(username) 66 | await message.answer('Хорошо, начнём сначала. Что вы хотите заказать?', reply_markup=work_type_kb) 67 | 68 | 69 | def check_work_type_readiness(callback): 70 | if callback.data == 'work_type_bot' or callback.data == 'work_type_site': 71 | username = '@' + callback.from_user.username 72 | return db.get_step(username)[0] == Step.CHOOSE_WORK_TYPE.value 73 | else: 74 | return False 75 | 76 | 77 | @dp.callback_query_handler(lambda callback: check_work_type_readiness(callback)) 78 | async def work_type(callback_query: types.CallbackQuery): 79 | username = '@' + callback_query.from_user.username 80 | 81 | if callback_query.data == 'work_type_bot': 82 | db.update_work_type(username, 'телеграм бот') 83 | elif callback_query.data == 'work_type_site': 84 | db.update_work_type(username, 'сайт') 85 | 86 | db.update_step(username=username, step=Step.ENTER_BUDGET.value) 87 | await bot.answer_callback_query(callback_query.id) 88 | await bot.send_message(callback_query.from_user.id, 'Хорошо, теперь определим бюджет проекта.\n' 89 | 'Если на наше усмотрение, то введите "-".\n' 90 | 'Или же введите сумму на которую расчитываете (в рублях):') 91 | 92 | 93 | @dp.message_handler(lambda message: db.get_step('@' + message.from_user.username)[0] == Step.ENTER_BUDGET.value) 94 | async def enter_budget(message: types.Message): 95 | username = '@' + message.from_user.username 96 | budget = message.text 97 | 98 | if not budget.isdigit() and budget != '-': 99 | await message.answer('Что-то пошло не так, попробуйте ещё раз!') 100 | 101 | else: 102 | if budget == '-': 103 | db.update_budget(username=username, budget='на ваше усмотрение') 104 | else: 105 | db.update_budget(username=username, budget=budget + 'р') 106 | 107 | db.update_step(username=username, step=Step.SEND_TASK_FILE.value) 108 | await message.answer('Отлично, теперь отправьте файл с техническим заданием:') 109 | 110 | 111 | @dp.message_handler(lambda m: db.get_step('@' + m.from_user.username)[0] == Step.SEND_TASK_FILE.value, 112 | content_types=types.ContentType.DOCUMENT) 113 | async def send_photo(message: types.Message): 114 | username = '@' + message.from_user.username 115 | 116 | doc_id = message.document.file_id 117 | file_info = await bot.get_file(doc_id) 118 | file_path = file_info.file_path 119 | file_name = message.document.file_name 120 | r = requests.get(f'https://api.telegram.org/file/bot{TOKEN}/{file_path}') 121 | 122 | while file_name in os.listdir('Tasks'): 123 | file_name = file_name.split('.') 124 | file_name[0] = file_name[0] + str(random.randrange(100)) 125 | file_name = '.'.join(file_name) 126 | 127 | with open(f'Tasks/{file_name}', 'wb') as f: 128 | f.write(r.content) 129 | 130 | db.update_task_file(username=username, task_file=file_name) 131 | db.update_step(username=username, step=Step.COMPLETE.value) 132 | await message.answer('Спасибо за ваш заказ.\nВ скором времени мы с вами свяжемся.') 133 | 134 | client_info = db.get_client_data(username) 135 | client_username = client_info[2] 136 | client_work_type = client_info[3] 137 | client_budget = client_info[4] 138 | client_task_file = open('Tasks\\' + client_info[5], 'rb') 139 | 140 | await bot.send_document(chat_id=CHAT_OUTPUT, document=client_task_file, caption=f'Техническое задание👆👆👆\n' 141 | f'Ссылка на пользователя:' 142 | f' {client_username}\nТип работы: ' 143 | f' {client_work_type}\n' 144 | f'Бюджет проекта: {client_budget}', 145 | reply_markup=order_answer_kb) 146 | 147 | 148 | @dp.callback_query_handler(lambda callback: str(callback.message.chat.id) == CHAT_OUTPUT) 149 | async def order_answer(callback_query: types.CallbackQuery): 150 | client_username = callback_query.message.caption.split('\n')[1].split(': ')[1] 151 | client_chat_id = db.get_client_data(client_username)[1] 152 | order_status = db.get_order_status(client_username)[0] 153 | 154 | if callback_query.data == 'order_answer_accept': 155 | if order_status == 1: 156 | await bot.answer_callback_query(callback_query_id=callback_query.id, 157 | text='Заказ уже принят!') 158 | return 159 | else: 160 | client_work_type = db.get_client_data(client_username)[3] 161 | client_budget = db.get_client_data(client_username)[4] 162 | client_task_file = db.get_client_data(client_username)[5] 163 | 164 | spreadsheets_data = [client_username, client_work_type, client_budget, client_task_file] 165 | 166 | credentials = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS_FILE, [ 167 | 'https://www.googleapis.com/auth/spreadsheets', 168 | 'https://www.googleapis.com/auth/drive']) 169 | http_auth = credentials.authorize(httplib2.Http()) 170 | service = googleapiclient.discovery.build('sheets', 'v4', http=http_auth) 171 | 172 | busy = True 173 | cell_number = 1 174 | 175 | while busy: 176 | ranges = [f'заказы!A{cell_number}:d{cell_number}'] 177 | 178 | result = service.spreadsheets().values().batchGet(spreadsheetId=SPREAD_SHEET_ID, 179 | ranges=ranges, 180 | valueRenderOption='FORMATTED_VALUE', 181 | dateTimeRenderOption='FORMATTED_STRING').execute() 182 | 183 | if 'values' in result['valueRanges'][0]: 184 | cell_number += 1 185 | 186 | else: 187 | ranges = f'заказы!A{cell_number}:D{cell_number}' 188 | 189 | service.spreadsheets().values().batchUpdate(spreadsheetId=SPREAD_SHEET_ID, 190 | body={ 191 | 'valueInputOption': 'USER_ENTERED', 192 | 'data': [ 193 | { 194 | 'range': ranges, 195 | 'majorDimension': 'ROWS', 196 | 'values': [ 197 | spreadsheets_data 198 | ] 199 | } 200 | ] 201 | }).execute() 202 | 203 | busy = False 204 | 205 | db.active_order(client_username) 206 | await bot.send_message(client_chat_id, 'Ваш заказ принят. В скором времени мы с вами свяжемся!') 207 | 208 | elif callback_query.data == 'order_answer_dismiss': 209 | if order_status == 1: 210 | await bot.answer_callback_query(callback_query_id=callback_query.id, 211 | text='Не возможно отказаться, вы уже приняли этот заказ!') 212 | return 213 | else: 214 | db.reset_data(client_username) 215 | await callback_query.message.delete() 216 | await bot.send_message(client_chat_id, 'Ваш заказ отклонён.') 217 | 218 | await bot.answer_callback_query(callback_query.id) 219 | 220 | 221 | if __name__ == '__main__': 222 | executor.start_polling(dispatcher=dp) 223 | --------------------------------------------------------------------------------