From cfd481d4d898a8ea6b26aa94b764d00748097e2c Mon Sep 17 00:00:00 2001 From: pinbraerts Date: Wed, 18 Nov 2020 23:30:08 +0300 Subject: [PATCH 1/5] Refactor user answers to routes --- route.py | 68 ++++++++++++++ user.py | 263 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 237 insertions(+), 94 deletions(-) create mode 100644 route.py diff --git a/route.py b/route.py new file mode 100644 index 0000000..4d37eb8 --- /dev/null +++ b/route.py @@ -0,0 +1,68 @@ +from functools import partial +from typing import List, Callable, Tuple, Optional +from itertools import chain +from functools import wraps + + +class Route: + def __init__(self, func: Callable, name: str, description: str = None, help: str = None, aliases: List[str] = None): + if description is None: + description = "" + if help is None: + help = "" + if aliases is None: + aliases = [] + + self.name = name + self.description = description + self.help = help + self.aliases = aliases + self.func = func + + def __set_name__(self, owner, _): + # register route in user + owner.routes.append(self) + + def __get__(self, obj, cls): + # AAAAAAAAAA + # bound method emulation + class BoundRoute: + def __init__(self, obj, route: Route): + object.__setattr__(self, 'obj', obj) + object.__setattr__(self, 'route', route) + + def __call__(self, *args, **kwargs): + return self.func(object.__getattribute__(self, 'obj'), *args, **kwargs) + + def __getattr__(self, attr): + return getattr(object.__getattribute__(self, 'route'), attr) + + def __setattr__(self, attr, value): + return setattr(object.__getattribute__(self, 'route'), attr, value) + + return BoundRoute(obj, self) + + def __repr__(self): + return "Route {{ name = {name}, description = {description}, help = {help}, aliases = {aliases} }}".format(self.__dict__) + + +@wraps(Route) +def route(*args, **kwargs): + return partial(Route, *args, **kwargs) + + +class Router: + routes: List[Route] = [] + + def find_route(self, line: str) -> Optional[Tuple[Route, str]]: + ''' + Checks if line starts with any of known routes + Returns: route and rest of the line if found, None otherwise + ''' + + low = line.lower() + for route in self.routes: + for variant in chain([route.name], route.aliases): + if low.startswith(variant): + return route, line[len(variant):].strip() + \ No newline at end of file diff --git a/user.py b/user.py index b8c6938..48a1e1f 100644 --- a/user.py +++ b/user.py @@ -1,37 +1,10 @@ from currency import Currency from product import Product from taxcom_parser import get_items +from route import route, Router -class User: - HELP_MESSAGES = {'all': "Список доступных команд:\n" - "1) Баланс -- узнать балансы пользователей группы\n" - "2) Купил -- зарегистрировать покупку\n" - "3) Чек -- загрузить чек\n" - "4) Подтвердить все -- подтверждение всех покупок\n\n" - "Для получения более подробной информации о команде наберите: Помощь [команда]", - 'error': "Неверный синтаксис команды", - 'баланс': "С помощью этой команды можно узнать балансы всех пользователей вашей группы", - 'купил': "С помощью этой команды можно зарегистрировать покупку. После введения необходимых данных " - "вам и всем указанным потребителям будет выслан запрос на подтверждение покупки. " - "Когда все участники подтвердят покупку, будет произведено обновление их балансов (стоимость " - "покупки распределяется равномерно по потребителям). В случае отказа хотя бы одного участника " - "покупка считается недействительной. Тогда необходимо устранить причину разногласий участников и " - "заново ввести команду.\n\n" - "Синтаксис:\n\n" - "Купил [потреб. 1] [потреб. 2] | все ...\n" - "[продукт 1] [стоимость]\n" - "[продукт 2] [стоимость]\n" - "...", - "чек": "С помощью этой команды можно загрузить чек. По введенным данным чек будет искаться в базе сайта " - "receipt.taxcom.ru. В случае успеха продукты, указанные в чеке, будут отправлены вам в формате, " - "подходящем для команды \"купил\"\n\n" - "Синтаксис:\n\n" - "1-ый вариант: Чек [ФПД] [сумма расчета]\n" - "2-ой вариант: Чек [данные из QR-кода]", - "подтвердить все": "Подтверждение всех покупок, в которых вы участвуете" - } - +class User(Router): def __init__(self, group, name, peer_id, balance=0): self.group = group self.name = name @@ -46,79 +19,181 @@ def to_json(self): } def answer(self, message): - lines = [line.strip().split(' ') for line in message["text"].split('\n')] - lines[0][0] = lines[0][0].lower() + lines = [x.strip() for x in message["text"].split("\n")] if not lines: - pass - elif lines[0][0] == "помощь" and len(lines[0]) > 1: - if " ".join(lines[0][1:]).lower() in self.HELP_MESSAGES: - self.send(self.HELP_MESSAGES[" ".join(lines[0][1:]).lower()]) - else: - self.send(self.HELP_MESSAGES['error'] + ": команды \"%s\" не существует" % lines[0][1].lower()) - elif lines[0][0] == "помощь": - self.send(self.HELP_MESSAGES['all']) - elif "подтвердить все" in " ".join(lines[0]): - for transaction_id, transaction in tuple(self.group.transactions_by_id.items()): - if self in transaction.confirmers: - self.group.confirm_transaction(self, transaction_id) - self.send("Все покупки, в которых вы участвуете, успешно подтверждены\n") - elif lines[0][0] == "купил": - if len(lines[0]) == 1: - self.send(self.HELP_MESSAGES['error'] + "\nУкажите потребителей") - return - consumers = lines[0][1:] - if len(lines) == 1: - self.send(self.HELP_MESSAGES['error'] + "\nУкажите продукты") - return - products = [] - for line in lines[1:]: - try: - products.append(Product(' '.join(line[:-1]), line[-1])) - except: - self.send(self.HELP_MESSAGES['error'] + ": ошибка в продукте \"%s\"" % ' '.join(line)) - return + return + + a = self.find_route(lines[0]) + if a is None or len(a) < 2: + self.send("Неизвестная команда") + self.do_help() + return + route, line = a + if not line: + lines.pop(0) + else: + lines[0] = line + try: + route.func(self, message, *lines) + except TypeError: + self.error_wrong_syntax() + + def error_wrong_syntax(self, info = None): + msg = "Неверный синтаксис команды" + if info is not None: + msg += ":\n" + info + self.send(msg) - self.group.create_transaction(self, products, consumers, message["id"]) - elif lines[0][0] == "чек": - if len(lines[0]) == 3: - fiscal_id = lines[0][1] - receipt_sum = lines[0][2] - qr_data = "&s=%s&fp=%s" % (receipt_sum, fiscal_id) - elif len(lines[0]) == 2: - qr_data = lines[0][1] - else: - self.send(self.HELP_MESSAGES['error'] + ". Введите необходимые данные") - return - products = [Product(' '.join(p[0].split(' ')), p[1]) for p in get_items(qr_data)] - if products: - self.send('\n'.join(map(str, products))) - else: - self.send("Чек не найден, проверьте введенные данные") - elif lines[0][0] == "баланс": - self.send( - "\n".join( - "Баланс {}: {}₽".format(user.name, user.balance) - for user in self.group.users_by_name.values() - ) + @route( + name = "помощь", + description = "получить список и синтаксис команд", + aliases = ["?"] + ) + def do_help(self, message = None, command = None): + if not self.do_help.help: # lazy auto help + msg = "Список доступных команд:\n" + for i, route in enumerate(self.routes, 1): + msg += "{0}) {1} -- {2}\n".format(i, route.name.capitalize(), route.description.capitalize()) + msg += "\nДля получения более подробной информации о команде наберите: Помощь [команда]" + self.do_help.help = msg + + if command is None: + return self.send(self.do_help.help) + + while command: + route, line = self.find_route(command) + if route is None or line is None: + return self.error_wrong_syntax("команды {} не существует".format(command)) + + self.send(route.help) + command = line + + @route( + name = "баланс", + description = "узнать балансы пользователей группы", + help = "С помощью этой команды можно узнать балансы всех пользователей вашей группы", + aliases = ["$"] + ) + def do_balance(self, message = None): + self.send( + "\n".join( + "Баланс {}: {}₽".format(user.name, user.balance) + for user in self.group.users_by_name.values() ) - elif lines[0][0] == "да" or lines[0][0] == "нет": - if message['reply_message']['from_id'] != self.id and "Подтверждаете покупку?" in message['reply_message'][ - 'text']: - transaction_id = message['reply_message']['fwd_messages'][0]['id'] - try: - if lines[0][0] == "да": + ) + + @route( + name = "купил", + description = "зарегистрировать покупку", + help = "С помощью этой команды можно зарегистрировать покупку. После введения необходимых данных " + "вам и всем указанным потребителям будет выслан запрос на подтверждение покупки. " + "Когда все участники подтвердят покупку, будет произведено обновление их балансов (стоимость " + "покупки распределяется равномерно по потребителям). В случае отказа хотя бы одного участника " + "покупка считается недействительной. Тогда необходимо устранить причину разногласий участников и " + "заново ввести команду.\n\n" + "Синтаксис:\n\n" + "Купил [потреб. 1] [потреб. 2] | все ...\n" + "[продукт 1] [стоимость]\n" + "[продукт 2] [стоимость]\n" + "..." + ) + def do_bought(self, message, consumers = None, *lines): + if consumers is None: + return self.error_wrong_syntax("Укажите потребителей") + if not lines: + return self.error_wrong_syntax("Укажите продукты") + consumers = consumers.split(' ') + products = [] + for line in lines: + try: + products.append(Product(*line.rsplit(' ', 1))) + except: + return self.error_wrong_syntax("ошибка в продукте {}".format(repr(line))) + + self.group.create_transaction(self, products, consumers, message["id"]) + + @route( + name = "чек", + description = "загрузить чек", + help = "С помощью этой команды можно загрузить чек. По введенным данным чек будет искаться в базе сайта " + "receipt.taxcom.ru. В случае успеха продукты, указанные в чеке, будут отправлены вам в формате, " + "подходящем для команды \"купил\"\n\n" + "Синтаксис:\n\n" + "1-ый вариант: Чек [ФПД] [сумма расчета]\n" + "2-ой вариант: Чек [данные из QR-кода]" + ) + def do_recipe(self, message, check): + s = check.split(' ') + qr_data = None + if len(s) == 2: + fiscal_id, receipt_sum = s + qr_data = "&s={0}&fp={1}".format(receipt_sum, fiscal_id) + elif len(s) == 1: + qr_data = check + else: + return self.error_wrong_syntax("Введите необходимые данные") + products = [Product(' '.join(p[0].split(' ')), p[1]) for p in get_items(qr_data)] + if products: + self.send('\n'.join(map(str, products))) + else: + self.send("Чек не найден, проверьте введенные данные") + + @route( + name = "подтвердить", + description = "подтвердить покупки", + help = "", # TODO + aliases = ["yes", "д", "+", "-", "y"] + ) + def do_accept(self, message, all = None): + if all is not None: + if all in ("все", "всё"): + for transaction_id, transaction in tuple(self.group.transactions_by_id.items()): + if self in transaction.confirmers: self.group.confirm_transaction(self, transaction_id) - elif lines[0][0] == "нет": + self.send("Все покупки, в которых вы участвуете, успешно подтверждены") + else: + self.error_wrong_syntax() # TODO + return + + if message['reply_message']['from_id'] != self.id and\ + "Подтверждаете покупку?" in message['reply_message']['text']: + transaction_id = message['reply_message']['fwd_messages'][0]['id'] + try: + self.group.confirm_transaction(self, transaction_id) + except KeyError: + self.send("Покупка уже завершена") + else: + self.send("Неизвестная команда. Если вы хотите ответить на вопрос, который не является последним" + " сообщением, то необходимо использовать функцию вк \"ответить\"") + + @route( + name = "отменить", + description = "отменить покупки", + help = "", # TODO + aliases = ["нет", "н", "no", "n", "-"] + ) + def do_refuse(self, message, all = None): + if all is not None: + if all in ("все", "всё"): + for transaction_id, transaction in tuple(self.group.transactions_by_id.items()): + if self in transaction.confirmers: self.group.decline_transaction(self, transaction_id) - except KeyError: - self.send("Покупка уже завершена") + self.send("Все покупки, в которых вы участвуете, отменены") else: - self.send("Неизвестная команда. Если вы хотите ответить на вопрос, который не является последним" - " сообщением, то необходимо использовать функцию вк \"ответить\"") + self.error_wrong_syntax() # TODO + return + + if message['reply_message']['from_id'] != self.id and\ + "Подтверждаете покупку?" in message['reply_message']['text']: + transaction_id = message['reply_message']['fwd_messages'][0]['id'] + try: + self.group.decline_transaction(self, transaction_id) + except KeyError: + self.send("Покупка уже завершена") else: - self.send("Неизвестная команда") - self.send(self.HELP_MESSAGES['all']) + self.send("Неизвестная команда. Если вы хотите ответить на вопрос, который не является последним" + " сообщением, то необходимо использовать функцию вк \"ответить\"") def send(self, message): return self.group.send(self, message) From 2d135a343cd1f83b008b470b7a27619475c1f47a Mon Sep 17 00:00:00 2001 From: pinbraerts Date: Wed, 18 Nov 2020 23:45:30 +0300 Subject: [PATCH 2/5] Delegate all vk stuff to Bot --- bot.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ group.py | 34 ++++++++++------------------------ main.py | 6 +++++- 3 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 bot.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..4acada3 --- /dev/null +++ b/bot.py @@ -0,0 +1,50 @@ +import vk +import random + + +class Bot: + def __init__(self): + self.session = vk.Session(access_token=open("key.txt").read().strip()) + self.api = vk.API(self.session) + self.vk_version = "5.103" + + def get_user(self, user_id): + return self.api.users.get( + v=self.vk_version, + user_id=user_id + ) + + def send(self, user, message, *args, **kwargs): + return self.api.messages.send( + v=self.vk_version, + peer_id=user.id, + random_id=random.random(), + message=message, + *args, **kwargs + ) + + def get_conversations(self, filter="unread"): + return self.api.messages.getConversations( + v=self.vk_version, + filter=filter + ) + + def get_history(self, peer_id, start_message, count): + return self.api.messages.getHistory( + v=self.vk_version, + peer_id=peer_id, + start_message_id=start_message, + count=count, + ) + + def get_messages(self, message_ids): + return self.api.messages.getById( + v=self.vk_version, + message_ids=message_ids + )['items'] + + def mark_as_read(self, peer_id): + return self.api.messages.markAsRead( + v=self.vk_version, + peer_id=peer_id + ) diff --git a/group.py b/group.py index 12a889e..1b41163 100644 --- a/group.py +++ b/group.py @@ -14,10 +14,8 @@ def to_json(o): class Group: - def __init__(self): - self.session = vk.Session(access_token=open("key.txt").read().strip()) - self.api = vk.API(self.session) - self.vk_version = "5.103" + def __init__(self, bot): + self.bot = bot self.users = [] self.users_by_name = dict() self.users_by_id = dict() @@ -77,10 +75,7 @@ def save_data(self): os.rename("data_tmp.json", "data.json") def add_user(self, peer_id): - usr = self.api.users.get( - v=self.vk_version, - user_id=peer_id - ) + usr = self.bot.get_user(peer_id) user = User(self, usr[0]["first_name"], peer_id) self.users.append(user) @@ -109,8 +104,7 @@ def run(self): self.save_data() def send(self, user, message, *args, **kwargs): - return self.api.messages.send( - v=self.vk_version, + return self.bot.send( peer_id=user.id, random_id=random.random(), message=message, @@ -200,10 +194,7 @@ def create_transaction(self, purchaser, products, names, message_id): def idle(self): try: - result = self.api.messages.getConversations( - v=self.vk_version, - filter="unread" - ) + result = self.bot.get_conversations("unread") except requests.exceptions.ConnectionError: time.sleep(300) except requests.exceptions.ReadTimeout: @@ -215,9 +206,8 @@ def idle(self): conv = conv["conversation"] peer_id = conv["peer"]["id"] - history = self.api.messages.getHistory( - v=self.vk_version, - peer_id=peer_id, + history = self.bot.get_history( + peer_id, start_message_id=max(conv["in_read"] + 1, conv["out_read"] + 1), count=conv["unread_count"] + 1, ) @@ -232,13 +222,9 @@ def idle(self): if 'reply_message' not in messages[i]: messages[i]['reply_message'] = messages[i - 1] else: - messages[i]['reply_message'] = self.api.messages.getById( - v=self.vk_version, - message_ids=[messages[i]['reply_message']['id']] + messages[i]['reply_message'] = self.bot.get_messages( + [messages[i]['reply_message']['id']] )['items'][0] user.answer(messages[i]) - self.api.messages.markAsRead( - v=self.vk_version, - peer_id=peer_id - ) + self.bot.mark_as_read(peer_id) diff --git a/main.py b/main.py index 7eb988f..1740a69 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,9 @@ from group import Group +from bot import Bot import sys import os + def lowpriority(): """ Set the priority of the process to below-normal.""" try: @@ -20,13 +22,15 @@ def lowpriority(): else: os.nice(19) + def main(): if len(sys.argv) != 2: print("Error. Write data directory") return lowpriority() os.chdir(sys.argv[1]) - app = Group() + bot = Bot() + app = Group(bot) app.run() From 23910f54999e5d2a9ef42de1e7ee53aec1a2199d Mon Sep 17 00:00:00 2001 From: pinbraerts Date: Thu, 19 Nov 2020 01:19:05 +0300 Subject: [PATCH 3/5] Fix bug --- user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user.py b/user.py index 48a1e1f..f7aa257 100644 --- a/user.py +++ b/user.py @@ -143,7 +143,7 @@ def do_recipe(self, message, check): name = "подтвердить", description = "подтвердить покупки", help = "", # TODO - aliases = ["yes", "д", "+", "-", "y"] + aliases = ["да", "д", "+", "yes", "y"] ) def do_accept(self, message, all = None): if all is not None: From 63750ce50cd90e947be95a925d0048aed1b73028 Mon Sep 17 00:00:00 2001 From: pinbraerts Date: Thu, 19 Nov 2020 01:19:39 +0300 Subject: [PATCH 4/5] Move bot-independent group logic into base class --- group.py | 133 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 55 deletions(-) diff --git a/group.py b/group.py index 1b41163..d89e07e 100644 --- a/group.py +++ b/group.py @@ -13,9 +13,8 @@ def to_json(o): return o.to_json() -class Group: - def __init__(self, bot): - self.bot = bot +class BaseGroup: + def __init__(self): self.users = [] self.users_by_name = dict() self.users_by_id = dict() @@ -36,15 +35,12 @@ def load(self, stream): res = json.load(stream) for u in res["users"]: - u = User( + self.add_user(User( self, u["name"], u["id"], u["balance"] - ) - self.users.append(u) - self.users_by_name[u.name] = u - self.users_by_id[u.id] = u + )) for t in res["transactions"]: t = Transaction( @@ -55,6 +51,11 @@ def load(self, stream): t["message_id"] ) self.transactions_by_id[t.message_id] = t + + def add_user(self, user: User): + self.users.append(user) + self.users_by_name[user.name] = user + self.users_by_id[user.id] = user def save(self, stream): json.dump( @@ -65,18 +66,76 @@ def save(self, stream): default=to_json ) + def save_data(self): + with open("data_tmp.json", "w", encoding="utf-8") as file: + self.save(file) + os.remove("data.json") + os.rename("data_tmp.json", "data.json") + + def create_transaction(self, purchaser, products, names, message_id): + consumers = [] + + for name in names: + if name in self.users_by_name: + consumers.append(self.users_by_name[name]) + elif name in ("все", "всем"): + consumers = self.users + break + else: + return # TODO: add error raise and handle in subclass + + confirmers = consumers.copy() + if purchaser not in consumers: + confirmers.append(purchaser) + transaction = Transaction(purchaser, products, consumers, confirmers, message_id) + self.transactions_by_id[message_id] = transaction + self.save_data() + return transaction + + def decline_transaction(self, user, transaction_id): + transaction = self.transactions_by_id[transaction_id] + del self.transactions_by_id[transaction_id] + self.save_data() + return transaction + + def confirm_transaction(self, user, transaction_id): + transaction = self.transactions_by_id[transaction_id] + if user not in transaction.confirmers: + return # TODO: add error raise and handle in subclass + transaction.confirmers.remove(user) + + if not transaction.confirmers: + self.make_transaction(transaction) + del self.transactions_by_id[transaction.message_id] + self.save_data() + return transaction + + def make_transaction(self, transaction): + val_per_usr = transaction.value / len(transaction.consumers) + transaction.purchaser.balance += val_per_usr * len(transaction.consumers) + for user in transaction.consumers: + user.balance -= val_per_usr + self.save_data() + + +class Group(BaseGroup): + def __init__(self, bot): + super().__init__() + self.bot = bot + def save_data(self): try: - self.save(open("data_tmp.json", "w", encoding='utf-8')) + super().save_data() except Exception as e: self.send_admins(e) - else: - os.remove("data.json") - os.rename("data_tmp.json", "data.json") - def add_user(self, peer_id): - usr = self.bot.get_user(peer_id) - user = User(self, usr[0]["first_name"], peer_id) + def add_user(self, user): + if isinstance(user, User): + return super().add_user(user) + + peer_id = user + user = self.bot.get_user(peer_id) + user = User(self, user[0]["first_name"], peer_id) self.users.append(user) self.users_by_name[user.name] = user @@ -115,25 +174,10 @@ def send_admins(self, message): for id in self.admins_id: self.send(self.users_by_id[id], message) - def make_transaction(self, transaction): - val_per_usr = transaction.value / len(transaction.consumers) - transaction.purchaser.balance += val_per_usr * len(transaction.consumers) - for user in transaction.consumers: - user.balance -= val_per_usr - self.save_data() - def confirm_transaction(self, user, transaction_id): - transaction = self.transactions_by_id[transaction_id] - if user not in transaction.confirmers: - self.send( - user, - "Покупка уже была подтверждена\n" - ) - return - transaction.confirmers.remove(user) + transaction = super().confirm_transaction(user, transaction_id) if not transaction.confirmers: - self.make_transaction(transaction) self.send( transaction.purchaser, "Покупка подтверждена\n", @@ -146,13 +190,9 @@ def confirm_transaction(self, user, transaction_id): for user in self.users_by_name.values() ) ) - del self.transactions_by_id[transaction.message_id] - self.save_data() def decline_transaction(self, user, transaction_id): - transaction = self.transactions_by_id[transaction_id] - del self.transactions_by_id[transaction_id] - self.save_data() + transaction = super().decline_transaction(user, transaction_id) for consumer in transaction.consumers: self.send( consumer, @@ -167,25 +207,8 @@ def decline_transaction(self, user, transaction_id): ) def create_transaction(self, purchaser, products, names, message_id): - consumers = [] - - for name in names: - if name in self.users_by_name: - consumers.append(self.users_by_name[name]) - elif name in ("все", "всем"): - consumers = self.users - break - else: - purchaser.send("Пользователя с именем \"%s\" нет в вашей группе" % name) - return - - confirmers = consumers.copy() - if purchaser not in consumers: - confirmers.append(purchaser) - transaction = Transaction(purchaser, products, consumers, confirmers, message_id) - self.transactions_by_id[message_id] = transaction - self.save_data() - for user in confirmers: + transaction = super().create_transaction(purchaser, products, names, message_id) + for user in transaction.confirmers: self.send( user, message="Подтверждаете покупку? Всего {}₽ (да/нет)".format(transaction.value), From c55c4b6d3dcad57a3b5b574a9470b360fdd1c45d Mon Sep 17 00:00:00 2001 From: pinbraerts Date: Thu, 19 Nov 2020 01:19:52 +0300 Subject: [PATCH 5/5] Add tests --- test.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test.py diff --git a/test.py b/test.py new file mode 100644 index 0000000..f31f6cf --- /dev/null +++ b/test.py @@ -0,0 +1,81 @@ +from group import BaseGroup +from user import User + + +class MockBot: + def __init__(self): + self.messages = dict() + self.msg_id = 0 + + def create_message(self, text, *args, **kwargs): + message = { + "text": text, + "id": self.msg_id + } + message.update(kwargs) + self.messages[self.msg_id] = message + self.msg_id += 1 + return message + + +class MockGroup(BaseGroup): + def __init__(self): + super().__init__() + + def save_data(self): + pass + + def send(self, user, message): + print(message) + + def confirm_transaction(self, user, transaction_id): + transaction = super().confirm_transaction(user, transaction_id) + print("Confirm transaction: transaction = {}".format(transaction)) + + def decline_transaction(self, user, transaction_id): + transaction = super().decline_transaction(user, transaction_id) + print("Cancel transaction: transaction = {}".format(transaction)) + + def create_transaction(self, purchaser, products, consumers, message_id): + super().create_transaction(purchaser, products, consumers, message_id) + print("Create transaction: purchaser = {0}, products = {1}, consumers = {2}, message_id = {3}" + .format(purchaser, products, consumers, message_id)) + + +bot = MockBot() +group = MockGroup() +group.load(open("data.json", encoding='utf-8')) +user = group.users_by_name["Дима"] + +user.answer(bot.create_message("?")) +user.answer(bot.create_message("? ?")) +user.answer(bot.create_message("? $")) +user.answer(bot.create_message("? купил")) +user.answer(bot.create_message("? чек")) +user.answer(bot.create_message("? +")) +user.answer(bot.create_message("? -")) +user.answer(bot.create_message("$")) +user.answer(bot.create_message("чек 2478417427 1532.00")) +user.answer(bot.create_message("чек &s=1532.00&fn=9251440300215132&fp=2478417427")) +user.answer(bot.create_message("купил Ваня\nмолоко аыфдвлаодылва 100\nпомидоры алыдвофдалофыду 200")) + +msg_buy1 = bot.create_message("купил Дима Ваня\nмолоко аыфдвлаодылва 100\nпомидоры алыдвофдалофыду 200") +user.answer(msg_buy1) +msg_answer1 = bot.create_message( + "Подтверждаете покупку?", + from_id=0, + fwd_messages=[msg_buy1] +) + +msg_buy2 = bot.create_message("купил все\nмолоко аыфдвлаодылва 100\nпомидоры алыдвофдалофыду 200") +user.answer(msg_buy2) +msg_answer2 = bot.create_message( + "Подтверждаете покупку?", + from_id=0, + fwd_messages=[msg_buy2] +) + +user.answer(bot.create_message("+", reply_message=msg_answer1)) +user.answer(bot.create_message("-", reply_message=msg_answer2)) +user.answer(bot.create_message("+ всё")) +user.answer(bot.create_message("- все"))