xync-bot 0.3.24.dev8__tar.gz → 0.3.24.dev11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xync-bot might be problematic. Click here for more details.

Files changed (31) hide show
  1. {xync_bot-0.3.24.dev8/xync_bot.egg-info → xync_bot-0.3.24.dev11}/PKG-INFO +1 -1
  2. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/__init__.py +1 -1
  3. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/pay/cd.py +1 -0
  4. xync_bot-0.3.24.dev11/xync_bot/routers/pay/dep.py +244 -0
  5. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/pay/handler.py +21 -50
  6. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/pay/window.py +83 -33
  7. xync_bot-0.3.24.dev11/xync_bot/typs.py +0 -0
  8. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11/xync_bot.egg-info}/PKG-INFO +1 -1
  9. xync_bot-0.3.24.dev8/xync_bot/routers/__init__.py +0 -92
  10. xync_bot-0.3.24.dev8/xync_bot/routers/pay/dep.py +0 -122
  11. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/.env.dist +0 -0
  12. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/.gitignore +0 -0
  13. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/.pre-commit-config.yaml +0 -0
  14. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/makefile +0 -0
  15. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/pyproject.toml +0 -0
  16. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/setup.cfg +0 -0
  17. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/test_main.http +0 -0
  18. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/loader.py +0 -0
  19. /xync_bot-0.3.24.dev8/xync_bot/typs.py → /xync_bot-0.3.24.dev11/xync_bot/routers/__init__.py +0 -0
  20. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/cond/__init__.py +0 -0
  21. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/cond/func.py +0 -0
  22. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/main.py +0 -0
  23. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/order.py +0 -0
  24. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/photo.py +0 -0
  25. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/vpn.py +0 -0
  26. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/routers/xicon.png +0 -0
  27. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot/shared.py +0 -0
  28. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot.egg-info/SOURCES.txt +0 -0
  29. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot.egg-info/dependency_links.txt +0 -0
  30. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot.egg-info/requires.txt +0 -0
  31. {xync_bot-0.3.24.dev8 → xync_bot-0.3.24.dev11}/xync_bot.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-bot
3
- Version: 0.3.24.dev8
3
+ Version: 0.3.24.dev11
4
4
  Summary: Telegram bot with web app for xync net
5
5
  Author-email: Artemiev <mixartemev@gmail.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -5,8 +5,8 @@ from PGram import Bot
5
5
  from aiogram.client.default import DefaultBotProperties
6
6
  from x_model import init_db
7
7
 
8
- from xync_bot.routers import Store
9
8
  from xync_bot.routers.cond import cr as cr
9
+ from xync_bot.routers.pay.dep import Store
10
10
 
11
11
  # from xync_bot.routers.main import mr
12
12
  from xync_bot.routers.pay.handler import pay as pay
@@ -34,6 +34,7 @@ class Pm(CallbackData, prefix="pm"):
34
34
 
35
35
  class Ppo(CallbackData, prefix="ppo"):
36
36
  num: int
37
+ is_target: int # bool
37
38
 
38
39
 
39
40
  class PayNav(CallbackData, prefix="pay_nav"):
@@ -0,0 +1,244 @@
1
+ from asyncio import gather
2
+ from enum import IntEnum
3
+
4
+ from aiogram.exceptions import TelegramBadRequest
5
+ from aiogram.fsm.state import StatesGroup, State
6
+ from aiogram.types import Message, InlineKeyboardMarkup
7
+ from pyrogram.types import CallbackQuery
8
+ from tortoise.functions import Min
9
+ from x_auth.enums import Role
10
+ from x_model.func import ArrayAgg
11
+ from xync_schema import models
12
+
13
+
14
+ class Report(StatesGroup):
15
+ text = State()
16
+
17
+
18
+ class CredState(StatesGroup):
19
+ detail = State()
20
+ name = State()
21
+
22
+
23
+ class PaymentState(StatesGroup):
24
+ amount = State()
25
+ timer = State()
26
+ timer_active = State()
27
+
28
+
29
+ class ActionType(IntEnum):
30
+ """Цель (назначение) платежа (target)"""
31
+
32
+ sent = 1 # Отправил
33
+ received = 2 # Получил
34
+ not_received = 3 # Не получил
35
+
36
+
37
+ class PayStep(IntEnum):
38
+ """Цель (назначение) платежа (target)"""
39
+
40
+ t_type = 1 # Выбор типа
41
+ t_cur = 2 # Выбор валюты
42
+ t_coin = 3 # Выбор монеты
43
+ t_pm = 4 # Выбор платежки
44
+ t_ex = 5 # Выбор биржи
45
+ t_cred_dtl = 6 # Ввод номера карты
46
+ t_cred_name = 7 # Ввод имени
47
+ # t_addr = 8 # todo: позже добавим: Выбор/ввод крипто кошелька
48
+ t_amount = 9 # Ввод суммы
49
+ """ Источник платежа (source) """
50
+ s_type = 10 # Выбор типа
51
+ s_cur = 11 # Выбор типа
52
+ s_pm = 12 # Выбор типа
53
+ s_coin = 13 # Выбор типа
54
+ s_ex = 14 # Выбор типа
55
+ ppo = 15 # Выбор возможности разбивки платежа
56
+ urgency = 16 # Выбор срочности получения платежа
57
+ pending_send = 17 # Ожидание отправки (если мы платим фиатом)
58
+ pending_confirm = 18 # Ожидание пока на той стороне подтвердят получение нашего фиата (если мы платим фиатом)
59
+ pending_receive = 19 # Ожидание поступления (если мы получаем фиат)
60
+
61
+
62
+ flags = {
63
+ "RUB": "🇷🇺",
64
+ "THB": "🇹🇭",
65
+ "IDR": "🇮🇩",
66
+ "TRY": "🇹🇷",
67
+ "GEL": "🇬🇪",
68
+ "VND": "🇻🇳",
69
+ "AED": "🇦🇪",
70
+ "AMD": "🇦🇲",
71
+ "AZN": "🇦🇿",
72
+ "CNY": "🇨🇳",
73
+ "EUR": "🇪🇺",
74
+ "HKD": "🇭🇰",
75
+ "INR": "🇮🇳",
76
+ "PHP": "🇵🇭",
77
+ "USD": "🇺🇸",
78
+ }
79
+
80
+
81
+ class SingleStore(type):
82
+ _store = None
83
+
84
+ async def __call__(cls):
85
+ if not cls._store:
86
+ cls._store = super(SingleStore, cls).__call__()
87
+ cls._store.coins = {k: v for k, v in await models.Coin.all().order_by("ticker").values_list("id", "ticker")}
88
+ curs = {c.id: c for c in await models.Cur.filter(ticker__in=flags.keys()).order_by("ticker")}
89
+ cls._store.curs = curs
90
+ cls._store.exs = {k: v for k, v in await models.Ex.all().values_list("id", "name")}
91
+ cls._store.pmcurs = {
92
+ k: v
93
+ for k, v in await models.Pmex.filter(pm__pmcurs__cur_id__in=cls._store.curs.keys())
94
+ .annotate(sname=Min("name"))
95
+ .group_by("pm__pmcurs__id")
96
+ .values_list("pm__pmcurs__id", "sname")
97
+ }
98
+ cls._store.coinexs = {
99
+ c.id: [ex.ex_id for ex in c.coinexs] for c in await models.Coin.all().prefetch_related("coinexs")
100
+ }
101
+ cls._store.curpms = {
102
+ cur_id: ids
103
+ for cur_id, ids in await models.Pmcur.filter(cur_id__in=curs.keys())
104
+ .annotate(ids=ArrayAgg("id"))
105
+ .group_by("cur_id")
106
+ .values_list("cur_id", "ids")
107
+ }
108
+ cls._store.curpms = {
109
+ cur_id: ids
110
+ for cur_id, ids in await models.Pmcur.filter(cur_id__in=curs.keys())
111
+ .annotate(ids=ArrayAgg("id"))
112
+ .group_by("cur_id")
113
+ .values_list("cur_id", "ids")
114
+ }
115
+
116
+ return cls._store
117
+
118
+
119
+ class Store:
120
+ class Global(metaclass=SingleStore):
121
+ coins: dict[int, str] # id:ticker
122
+ curs: dict[int, models.Cur] # id:Cur
123
+ exs: dict[int, str] # id:name
124
+ coinexs: dict[int, list[int]] # id:[ex_ids]
125
+ pmcurs: dict[int, str] # pmcur_id:name
126
+ curpms: dict[int, list[int]] # id:[pmcur_ids]
127
+
128
+ class Permanent:
129
+ msg_id: int = None
130
+ user: models.User = None
131
+ actors: dict[int, int] = None # key=ex_id
132
+ creds: dict[int, models.Cred] = None # key=cred_id
133
+ cur_creds: dict[int, list[int]] = None # pmcur_id:[cred_ids]
134
+
135
+ class Current:
136
+ is_target: bool = True
137
+ is_fiat: bool = None
138
+ msg_to_del: Message = None
139
+
140
+ class Payment:
141
+ t_cur_id: int = None
142
+ s_cur_id: int = None
143
+ t_coin_id: int = None
144
+ s_coin_id: int = None
145
+ t_pmcur_id: int = None
146
+ s_pmcur_id: int = None
147
+ t_ex_id: int = None
148
+ s_ex_id: int = None
149
+ amount: int | float = None
150
+ ppo: int = 1
151
+ addr_id: int = None
152
+ cred_dtl: str = None
153
+ cred_id: int = None
154
+ urg: int = 5
155
+ pr_id: int = None
156
+
157
+ glob: Global
158
+ perm: Permanent = Permanent()
159
+ pay: Payment = Payment()
160
+ curr: Current = Current()
161
+
162
+
163
+ async def fill_creds(person_id: int) -> tuple[dict[int, models.Cred], dict[int, list[int]]]:
164
+ cq = models.Cred.filter(person_id=person_id)
165
+ creds = {c.id: c for c in await cq}
166
+ cur_creds = {
167
+ pci: ids
168
+ for pci, ids in await cq.annotate(ids=ArrayAgg("id")).group_by("pmcur_id").values_list("pmcur_id", "ids")
169
+ }
170
+ return creds, cur_creds
171
+
172
+
173
+ async def fill_actors(person_id: int) -> dict[int, int]:
174
+ ex_actors = {
175
+ # todo: check len(ids) == 1
176
+ exi: ids[0]
177
+ for exi, ids in await models.Actor.filter(person_id=person_id)
178
+ .annotate(ids=ArrayAgg("id"))
179
+ .group_by("ex_id")
180
+ .values_list("ex_id", "ids")
181
+ }
182
+ return ex_actors
183
+
184
+
185
+ async def edit(msg: Message, txt: str, rm: InlineKeyboardMarkup):
186
+ await gather(msg.edit_text(txt), msg.edit_reply_markup(reply_markup=rm))
187
+
188
+
189
+ async def ans(cbq: CallbackQuery, txt: str = None):
190
+ await cbq.answer(txt, cache_time=0)
191
+
192
+
193
+ async def dlt(msg: Message):
194
+ await msg.delete()
195
+
196
+
197
+ async def edt(msg: Message, txt: str, rm: InlineKeyboardMarkup):
198
+ if msg.message_id == msg.bot.store.perm.msg_id:
199
+ await msg.edit_text(txt, reply_markup=rm)
200
+ else: # окно вызвано в ответ на текст, а не кнопку
201
+ try:
202
+ await msg.bot.edit_message_text(
203
+ txt, chat_id=msg.chat.id, message_id=msg.bot.store.perm.msg_id, reply_markup=rm
204
+ )
205
+ except TelegramBadRequest as e:
206
+ print(msg.bot.store.perm.msg_id, e)
207
+
208
+
209
+ async def have_coin_amount(store: Store) -> bool:
210
+ actor_id = store.perm.actors[store.pay.t_ex_id]
211
+ asset = await models.Asset.get_or_none(addr__coin_id=store.pay.t_coin_id, addr__actor_id=actor_id)
212
+ return store.pay.amount <= asset.free
213
+
214
+
215
+ async def need_ppo(store: Store):
216
+ cur_id = getattr(store.pay, ("t" if store.curr.is_target else "s") + "_cur_id")
217
+ usd_amount = store.pay.amount * store.glob.curs[cur_id].rate
218
+ if usd_amount < 50:
219
+ return 0
220
+ elif usd_amount > 100:
221
+ return 2
222
+ else:
223
+ return 1
224
+
225
+
226
+ async def get_in(store: Store) -> tuple[models.Addr | models.Cred, str]:
227
+ if store.pay.s_ex_id:
228
+ addr_in = await models.Addr.get(
229
+ actor__ex_id=store.pay.s_ex_id, coin_id=store.pay.s_coin_id, actor__user__role__gte=Role.ADMIN
230
+ ).prefetch_related("actor")
231
+ return addr_in, (
232
+ f"{store.glob.coins[store.pay.s_coin_id]} на {store.glob.exs[store.pay.s_ex_id]}, "
233
+ f"по id: `{addr_in.actor.exid}`"
234
+ )
235
+ else:
236
+ s_pmcur = await models.Pmcur.get(id=store.pay.s_pmcur_id).prefetch_related("pm__grp")
237
+ cred_id = await models.Cred.get( # todo: extend cred search conditions
238
+ **({"pmcur__pm__grp": s_pmcur.pm.grp} if s_pmcur.pm.grp else {"pmcur_id": store.pay.s_pmcur_id}),
239
+ person__user__role__gte=Role.ADMIN,
240
+ )
241
+ return cred_id, (
242
+ f"{store.glob.curs[store.pay.s_cur_id]} на {store.glob.pmcurs[store.pay.s_pmcur_id]} "
243
+ f"по номеру: `{cred_id.detail}`, имя: {cred_id.name}{cred_id.extra and f' ({cred_id.extra})' or ''}"
244
+ )
@@ -1,16 +1,14 @@
1
- from asyncio import create_task, gather
2
- from datetime import timedelta, datetime
1
+ from asyncio import gather
2
+ from datetime import datetime
3
3
 
4
4
  import PGram
5
5
  from aiogram import Router, F
6
6
  from aiogram.filters import Command
7
7
  from aiogram.types import Message, CallbackQuery
8
8
  from aiogram.fsm.context import FSMContext
9
- from xync_bot.routers.pay.dep import fill_creds, fill_actors, dlt, ans
9
+ from xync_bot.routers.pay.dep import fill_creds, fill_actors, dlt, ans, Store
10
10
  from xync_schema import models
11
- from aiogram.utils.keyboard import InlineKeyboardBuilder
12
11
 
13
- from xync_bot import Store
14
12
  from xync_bot.routers.pay import cd, dep, window
15
13
 
16
14
  pay = Router()
@@ -37,7 +35,7 @@ async def h_got_fiat_type(query: CallbackQuery, bot: PGram):
37
35
  async def h_got_crypto_type(query: CallbackQuery, bot: PGram):
38
36
  """Step 2c: Select coin"""
39
37
  bot.store.curr.is_fiat = False
40
- (bot.store.perm.actors, bot.store.perm.ex_actors), *_ = await gather(
38
+ bot.store.perm.actors, *_ = await gather(
41
39
  fill_actors(bot.store.perm.user.person_id), window.coin_select(query.message), ans(query, "Понял, крипта")
42
40
  )
43
41
 
@@ -92,7 +90,8 @@ async def h_got_cred_name(msg: Message, state: FSMContext):
92
90
  person_id=store.perm.user.person_id,
93
91
  pmcur_id=store.pay.t_pmcur_id,
94
92
  )
95
- msg.bot.store.pay.cred_id = cred.id
93
+ store.pay.cred_id = cred.id
94
+ store.perm.creds[cred.id] = cred
96
95
  await gather(window.amount(msg), dlt(msg), state.set_state(dep.PaymentState.amount))
97
96
 
98
97
 
@@ -102,11 +101,15 @@ async def h_got_ex(query: CallbackQuery, callback_data: cd.Ex, state: FSMContext
102
101
  store: Store = query.message.bot.store
103
102
  ist = store.curr.is_target
104
103
  setattr(store.pay, ("t" if ist else "s") + "_ex_id", callback_data.id)
105
- await gather(
106
- (window.amount if ist else window.set_ppo)(query.message),
107
- ans(query, f"Биржа {store.glob.exs[callback_data.id]} выбрана"),
108
- state.set_state(dep.PaymentState.amount),
109
- )
104
+ if ist:
105
+ await window.amount(query.message)
106
+ actor_id = store.perm.actors[store.pay.t_ex_id]
107
+ addr = await models.Addr.get(coin_id=store.pay.t_coin_id, actor_id=actor_id)
108
+ store.pay.addr_id = addr.id
109
+ else:
110
+ await window.set_ppo(query.message)
111
+ await ans(query, f"Биржа {store.glob.exs[callback_data.id]} выбрана")
112
+ await state.set_state(dep.PaymentState.amount)
110
113
 
111
114
 
112
115
  @pay.message(dep.PaymentState.amount)
@@ -130,7 +133,7 @@ async def h_got_source_pm(query: CallbackQuery, callback_data: cd.Pm):
130
133
  store.pay.s_pmcur_id = callback_data.pmcur_id
131
134
  await gather(
132
135
  window.set_ppo(query.message),
133
- ans(query, store.glob.pms[callback_data.pmcur_id]),
136
+ ans(query, store.glob.pmcurs[callback_data.pmcur_id]),
134
137
  )
135
138
 
136
139
 
@@ -141,39 +144,10 @@ async def h_got_ppo(query: CallbackQuery, callback_data: cd.Ppo):
141
144
 
142
145
 
143
146
  @pay.callback_query(cd.Time.filter())
144
- async def process_time_selection(query: CallbackQuery, callback_data: cd.Time, state: FSMContext):
145
- store: Store = query.message.bot.store
146
-
147
- pay_until = datetime.now() + timedelta(minutes=callback_data.minutes)
148
-
149
- if ex_id := (store.pay.t_ex_id or store.pay.s_ex_id):
150
- actor_id = store.perm.ex_actors[ex_id]
151
- if not (addr_id := store.pay.addr_id):
152
- coin_id = store.pay.t_coin_id or store.pay.s_coin_id
153
- addr_id = await models.Addr.get(coin_id=coin_id, actor_id=actor_id).values_list("id", flat=True)
154
- store.pay.addr_id = addr_id
155
- else:
156
- addr_id = None
157
- pr_data = dict(
158
- pay_until=pay_until,
159
- amount=store.pay.amount,
160
- parts=store.pay.ppo,
161
- payed_at=None,
162
- addr_id=addr_id,
163
- cred_id=store.pay.cred_id,
164
- user=store.perm.user,
165
- )
166
- (pay_req, _), *__ = await gather(
167
- models.PayReq.update_or_create(**pr_data), ans(query, None), state.set_state(dep.PaymentState.timer)
168
- )
169
-
170
- await state.update_data(
171
- timer=callback_data.minutes,
172
- timer_active=True,
173
- pay_until=pay_until,
174
- pay_req_id=pay_req.id,
175
- )
176
- create_task(window.run_timer(query.message, state))
147
+ async def h_got_urgency(query: CallbackQuery, callback_data: cd.Time):
148
+ query.message.bot.store.pay.urg = callback_data.minutes
149
+ await window.create_payreq(query.message)
150
+ await ans(query, f"Ok {callback_data.minutes} min.")
177
151
 
178
152
 
179
153
  # ACTIONS
@@ -187,11 +161,8 @@ async def payment_confirmed(query: CallbackQuery, state: FSMContext):
187
161
  pay_req = await models.PayReq.get(id=data["pay_req_id"])
188
162
  pay_req.payed_at = payed_at
189
163
  await pay_req.save()
190
-
191
- builder = InlineKeyboardBuilder()
192
- builder.button(text="Новый платеж💸", callback_data=cd.PayNav(to=cd.PayStep.t_type))
193
- await query.message.answer("✅ Платеж успешно подтвержден", reply_markup=builder.as_markup())
194
164
  await state.clear()
165
+ await window.success(query.message)
195
166
 
196
167
 
197
168
  @pay.callback_query(cd.Action.filter(F.act.__eq__(cd.ActionType.not_received)))
@@ -1,11 +1,13 @@
1
1
  from asyncio import sleep
2
+ from datetime import datetime, timedelta
2
3
 
3
4
  from aiogram.fsm.context import FSMContext
4
5
  from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
5
6
  from aiogram.utils.keyboard import InlineKeyboardBuilder
6
- from xync_bot.routers.pay.dep import edt
7
+ from xync_schema import models
8
+
9
+ from xync_bot.routers.pay.dep import edt, need_ppo, have_coin_amount, get_in, Store
7
10
 
8
- from xync_bot import Store
9
11
  from xync_bot.routers.pay import cd, dep
10
12
 
11
13
 
@@ -25,7 +27,16 @@ async def type_select(msg: Message):
25
27
  ]
26
28
  ]
27
29
  )
28
- txt = "Что нужно?" if store.curr.is_target else "Чем платишь?"
30
+ if store.curr.is_target:
31
+ txt = "Что нужно?"
32
+ else:
33
+ if store.pay.t_coin_id:
34
+ inf = f"{store.glob.coins[store.pay.t_coin_id]} на {store.glob.exs[store.pay.t_ex_id]}:{store.pay.addr_id}"
35
+ else:
36
+ cur = store.glob.curs[store.pay.t_cur_id].ticker
37
+ cred: models.Cred = store.perm.creds[store.pay.cred_id]
38
+ inf = f"{cur} на {store.glob.pmcurs[store.pay.t_pmcur_id]}: {cred.repr()}"
39
+ txt = f"Нужен платеж: {store.pay.amount} {inf}\nЧем будете платить?"
29
40
  if store.perm.msg_id:
30
41
  await edt(msg, txt, rm)
31
42
  else:
@@ -37,8 +48,8 @@ async def cur_select(msg: Message):
37
48
  """Common using cur func"""
38
49
  builder = InlineKeyboardBuilder()
39
50
  ist: bool = msg.bot.store.curr.is_target
40
- for cur_id, ticker in msg.bot.store.glob.curs.items():
41
- builder.button(text=ticker + dep.flags[ticker], callback_data=cd.Cur(id=cur_id, is_target=ist))
51
+ for cur_id, cur in msg.bot.store.glob.curs.items():
52
+ builder.button(text=cur.ticker + dep.flags[cur.ticker], callback_data=cd.Cur(id=cur_id, is_target=ist))
42
53
  builder.button(text="Назад к выбору типа", callback_data=cd.PayNav(to=cd.PayStep.t_type))
43
54
  builder.adjust(3, 3, 3, 3, 3, 1)
44
55
  sfx = "ую нужно" if ist else "ой платишь"
@@ -82,7 +93,7 @@ async def pm(msg: Message):
82
93
  cur_id = getattr(store.pay, ("t" if ist else "s") + "_cur_id")
83
94
  builder = InlineKeyboardBuilder()
84
95
  for pmcur_id in store.glob.curpms[cur_id]:
85
- builder.button(text=store.glob.pms[pmcur_id], callback_data=cd.Pm(pmcur_id=pmcur_id, is_target=ist))
96
+ builder.button(text=store.glob.pmcurs[pmcur_id], callback_data=cd.Pm(pmcur_id=pmcur_id, is_target=ist))
86
97
  builder.button(
87
98
  text="Назад к выбору валюты", callback_data=cd.PayNav(to=cd.PayStep.t_cur if ist else cd.PayStep.s_cur)
88
99
  )
@@ -99,10 +110,7 @@ async def fill_cred_dtl(msg: Message):
99
110
  if cred_ids := store.perm.cur_creds.get(store.pay.t_pmcur_id):
100
111
  for cred_id in cred_ids:
101
112
  cred = store.perm.creds[cred_id]
102
- txt = f"{cred.detail}\n{cred.name}"
103
- if cred.extra:
104
- txt += f" ({cred.extra})"
105
- builder.button(text=txt, callback_data=cd.Cred(id=cred_id))
113
+ builder.button(text=cred.repr(), callback_data=cd.Cred(id=cred_id))
106
114
  txt = "Выберите реквизиты куда нужно получить деньги, если в списке нет нужных, то\nв"
107
115
 
108
116
  builder.button(text="Назад к выбору платежной системы", callback_data=cd.PayNav(to=cd.PayStep.t_pm))
@@ -110,7 +118,7 @@ async def fill_cred_dtl(msg: Message):
110
118
  builder.adjust(2)
111
119
 
112
120
  await msg.edit_text(
113
- f"{txt}ведите номер для {store.glob.pms[store.pay.t_pmcur_id]}:", reply_markup=builder.as_markup()
121
+ f"{txt}ведите номер для {store.glob.pmcurs[store.pay.t_pmcur_id]}:", reply_markup=builder.as_markup()
114
122
  )
115
123
 
116
124
 
@@ -121,9 +129,9 @@ async def fill_cred_name(msg: Message):
121
129
  builder.adjust(2)
122
130
  store: Store = msg.bot.store
123
131
  cur = store.glob.curs[store.pay.t_cur_id]
124
- payment = store.glob.pms[store.pay.t_pmcur_id]
132
+ payment = store.glob.pmcurs[store.pay.t_pmcur_id]
125
133
  detail = store.pay.cred_dtl
126
- await edt(msg, f"{cur}:{payment}:{detail}: Введите имя получателя", builder.as_markup())
134
+ await edt(msg, f"{cur.ticker}:{payment}:{detail}: Введите имя получателя", builder.as_markup())
127
135
 
128
136
 
129
137
  async def amount(msg: Message):
@@ -131,9 +139,9 @@ async def amount(msg: Message):
131
139
  builder = InlineKeyboardBuilder()
132
140
  store: Store = msg.bot.store
133
141
  if store.curr.is_fiat:
134
- cur_coin = store.glob.curs[store.pay.t_cur_id]
142
+ cur_coin = store.glob.curs[store.pay.t_cur_id].ticker
135
143
  builder.button(text="Назад к вводу имени", callback_data=cd.PayNav(to=cd.PayStep.t_cred_name))
136
- t_name = store.glob.pms[store.pay.t_pmcur_id]
144
+ t_name = store.glob.pmcurs[store.pay.t_pmcur_id]
137
145
  else:
138
146
  cur_coin = store.glob.coins[store.pay.t_coin_id]
139
147
  builder.button(text="Назад к выбору биржи", callback_data=cd.PayNav(to=cd.PayStep.t_ex))
@@ -146,29 +154,62 @@ async def amount(msg: Message):
146
154
 
147
155
 
148
156
  async def set_ppo(msg: Message):
157
+ store: Store = msg.bot.store
158
+ ist = store.curr.is_target
159
+ if nppo := await need_ppo(store):
160
+ builder = InlineKeyboardBuilder()
161
+ builder.button(text="Нет", callback_data=cd.Ppo(num=1, is_target=ist)).button(
162
+ text="Да", callback_data=cd.Ppo(num=2, is_target=ist)
163
+ )
164
+ if nppo > 1:
165
+ builder.button(text="Да хоть 3мя", callback_data=cd.Ppo(num=3, is_target=ist))
166
+ builder.adjust(2)
167
+ await edt(msg, f"2мя платежами сможете {'принять' if ist else 'отравить'}?", builder.as_markup())
168
+ elif ist:
169
+ store.curr.is_target = False
170
+ await type_select(msg)
171
+ else:
172
+ await set_urgency(msg)
173
+
174
+
175
+ async def set_urgency(msg: Message):
176
+ store: Store = msg.bot.store
177
+ if not store.curr.is_fiat or have_coin_amount(store):
178
+ return await create_payreq(msg) # next
179
+ builder = InlineKeyboardBuilder()
180
+ (
181
+ builder.button(text="1 мин", callback_data=cd.Time(minutes=1))
182
+ .button(text="5 мин", callback_data=cd.Time(minutes=5))
183
+ .button(text="30 мин", callback_data=cd.Time(minutes=30))
184
+ .button(text="3 часа", callback_data=cd.Time(minutes=180))
185
+ .button(text="сутки", callback_data=cd.Time(minutes=60 * 24))
186
+ .button(text="Назад к вводу платежей", callback_data=cd.PayNav(to=cd.PayStep.t_pm))
187
+ .button(text="Домой", callback_data=cd.PayNav(to=cd.PayStep.t_type))
188
+ .adjust(2, 2, 1, 1, 1)
189
+ )
190
+ return await edt(msg, "Сколько можешь ждать?", builder.as_markup())
191
+
192
+
193
+ async def create_payreq(msg: Message):
194
+ store: Store = msg.bot.store
195
+ pay_req, _ = await models.PayReq.update_or_create(
196
+ {"pay_until": datetime.now() + timedelta(minutes=store.pay.urg)},
197
+ amount=store.pay.amount,
198
+ parts=store.pay.ppo,
199
+ addr_id=store.pay.addr_id,
200
+ cred_id=store.pay.cred_id,
201
+ user=store.perm.user,
202
+ )
203
+ store.pay.pr_id = pay_req.id
204
+ inp, txt = await get_in(store)
149
205
  rm = InlineKeyboardMarkup(
150
206
  inline_keyboard=[
151
- [
152
- InlineKeyboardButton(text="Нет", callback_data="ppo:1"),
153
- InlineKeyboardButton(text="Да", callback_data="ppo:2"),
154
- ],
155
- [InlineKeyboardButton(text="Да хоть на 3", callback_data="ppo:3")],
207
+ [InlineKeyboardButton(text="Отправил", callback_data=cd.Action(act=cd.ActionType.sent).pack())],
156
208
  ]
157
209
  )
158
- await msg.edit_text("На 2 платежа сможем разбить?", reply_markup=rm)
210
+ await edt(msg, f"Отправь {store.pay.amount}" + txt, rm.as_markup())
159
211
 
160
-
161
- async def set_urgency(msg: Message):
162
- builder = InlineKeyboardBuilder()
163
- builder.button(text="1 мин", callback_data=cd.Time(minutes=1))
164
- builder.button(text="5 мин", callback_data=cd.Time(minutes=5))
165
- builder.button(text="30 мин", callback_data=cd.Time(minutes=30))
166
- builder.button(text="3 часа", callback_data=cd.Time(minutes=180))
167
- builder.button(text="сутки", callback_data=cd.Time(minutes=60 * 24))
168
- builder.button(text="Назад к вводу платежей", callback_data=cd.PayNav(to=cd.PayStep.t_pm))
169
- builder.button(text="Домой", callback_data=cd.PayNav(to=cd.PayStep.t_type))
170
- builder.adjust(2, 2, 1, 1, 1)
171
- await msg.edit_text("Сколько можешь ждать?", reply_markup=builder.as_markup())
212
+ # create_task(window.run_timer(msg))
172
213
 
173
214
 
174
215
  async def run_timer(message, state: FSMContext):
@@ -215,3 +256,12 @@ async def run_timer(message, state: FSMContext):
215
256
  await message.edit_text("⏳ Время вышло!", reply_markup=builder.as_markup())
216
257
  except Exception:
217
258
  pass
259
+
260
+
261
+ async def success(msg: Message):
262
+ rm = InlineKeyboardMarkup(
263
+ inline_keyboard=[
264
+ [InlineKeyboardButton(text="Новый платеж💸", callback_data=cd.PayNav(to=cd.PayStep.t_type).pack())]
265
+ ]
266
+ )
267
+ await msg.edit_text("✅ Платеж успешно подтвержден", reply_markup=rm)
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-bot
3
- Version: 0.3.24.dev8
3
+ Version: 0.3.24.dev11
4
4
  Summary: Telegram bot with web app for xync net
5
5
  Author-email: Artemiev <mixartemev@gmail.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -1,92 +0,0 @@
1
- from aiogram.types import Message
2
- from tortoise.functions import Min
3
- from x_model.func import ArrayAgg
4
- from xync_schema import models
5
-
6
- from xync_bot.routers.pay.dep import flags
7
-
8
-
9
- class SingleStore(type):
10
- _store = None
11
-
12
- async def __call__(cls):
13
- if not cls._store:
14
- cls._store = super(SingleStore, cls).__call__()
15
- cls._store.coins = {k: v for k, v in await models.Coin.all().order_by("ticker").values_list("id", "ticker")}
16
- curs = {
17
- k: v
18
- for k, v in await models.Cur.filter(ticker__in=flags.keys())
19
- .order_by("ticker")
20
- .values_list("id", "ticker")
21
- }
22
- cls._store.curs = curs
23
- cls._store.exs = {k: v for k, v in await models.Ex.all().values_list("id", "name")}
24
- cls._store.pms = {
25
- k: v
26
- for k, v in await models.Pmex.filter(pm__pmcurs__cur_id__in=cls._store.curs.keys())
27
- .annotate(sname=Min("name"))
28
- .group_by("pm__pmcurs__id")
29
- .values_list("pm__pmcurs__id", "sname")
30
- }
31
- cls._store.coinexs = {
32
- c.id: [ex.ex_id for ex in c.coinexs] for c in await models.Coin.all().prefetch_related("coinexs")
33
- }
34
- cls._store.curpms = {
35
- cur_id: ids
36
- for cur_id, ids in await models.Pmcur.filter(cur_id__in=curs.keys())
37
- .annotate(ids=ArrayAgg("id"))
38
- .group_by("cur_id")
39
- .values_list("cur_id", "ids")
40
- }
41
- cls._store.curpms = {
42
- cur_id: ids
43
- for cur_id, ids in await models.Pmcur.filter(cur_id__in=curs.keys())
44
- .annotate(ids=ArrayAgg("id"))
45
- .group_by("cur_id")
46
- .values_list("cur_id", "ids")
47
- }
48
-
49
- return cls._store
50
-
51
-
52
- class Store:
53
- class Global(metaclass=SingleStore):
54
- coins: dict[int, str] # id:ticker
55
- curs: dict[int, str] # id:ticker
56
- exs: dict[int, str] # id:name
57
- coinexs: dict[int, list[int]] # id:[ex_ids]
58
- pms: dict[int, str] # pmcur_id:name
59
- curpms: dict[int, list[int]] # id:[pmcur_ids]
60
-
61
- class Permanent:
62
- msg_id: int = None
63
- user: models.User = None
64
- actors: dict[int, models.Actor] = None # key=actor_id
65
- ex_actors: dict[int, list[int]] = None # key=ex_id
66
- creds: dict[int, models.Cred] = None # key=cred_id
67
- cur_creds: dict[int, list[int]] = None # pmcur_id:[cred_ids]
68
-
69
- class Current:
70
- is_target: bool = None
71
- is_fiat: bool = None
72
- msg_to_del: Message = None
73
-
74
- class Payment:
75
- t_cur_id: int = None
76
- s_cur_id: int = None
77
- t_coin_id: int = None
78
- s_coin_id: int = None
79
- t_pmcur_id: int = None
80
- s_pmcur_id: int = None
81
- t_ex_id: int = None
82
- s_ex_id: int = None
83
- amount: int | float = None
84
- ppo: int = None
85
- addr_id: int = None
86
- cred_dtl: str = None
87
- cred_id: int = None
88
-
89
- glob: Global
90
- perm: Permanent = Permanent()
91
- pay: Payment = Payment()
92
- curr: Current = Current()
@@ -1,122 +0,0 @@
1
- from asyncio import gather
2
- from enum import IntEnum
3
-
4
- from aiogram.exceptions import TelegramBadRequest
5
- from aiogram.fsm.state import StatesGroup, State
6
- from aiogram.types import Message, InlineKeyboardMarkup
7
- from pyrogram.types import CallbackQuery
8
- from x_model.func import ArrayAgg
9
- from xync_schema import models
10
-
11
-
12
- class Report(StatesGroup):
13
- text = State()
14
-
15
-
16
- class CredState(StatesGroup):
17
- detail = State()
18
- name = State()
19
-
20
-
21
- class PaymentState(StatesGroup):
22
- amount = State()
23
- timer = State()
24
- timer_active = State()
25
-
26
-
27
- class ActionType(IntEnum):
28
- """Цель (назначение) платежа (target)"""
29
-
30
- sent = 1 # Отправил
31
- received = 2 # Получил
32
- not_received = 3 # Не получил
33
-
34
-
35
- class PayStep(IntEnum):
36
- """Цель (назначение) платежа (target)"""
37
-
38
- t_type = 1 # Выбор типа
39
- t_cur = 2 # Выбор валюты
40
- t_coin = 3 # Выбор монеты
41
- t_pm = 4 # Выбор платежки
42
- t_ex = 5 # Выбор биржи
43
- t_cred_dtl = 6 # Ввод номера карты
44
- t_cred_name = 7 # Ввод имени
45
- # t_addr = 8 # todo: позже добавим: Выбор/ввод крипто кошелька
46
- t_amount = 9 # Ввод суммы
47
- """ Источник платежа (source) """
48
- s_type = 10 # Выбор типа
49
- s_cur = 11 # Выбор типа
50
- s_pm = 12 # Выбор типа
51
- s_coin = 13 # Выбор типа
52
- s_ex = 14 # Выбор типа
53
- ppo = 15 # Выбор возможности разбивки платежа
54
- urgency = 16 # Выбор срочности получения платежа
55
- pending_send = 17 # Ожидание отправки (если мы платим фиатом)
56
- pending_confirm = 18 # Ожидание пока на той стороне подтвердят получение нашего фиата (если мы платим фиатом)
57
- pending_receive = 19 # Ожидание поступления (если мы получаем фиат)
58
-
59
-
60
- flags = {
61
- "RUB": "🇷🇺",
62
- "THB": "🇹🇭",
63
- "IDR": "🇮🇩",
64
- "TRY": "🇹🇷",
65
- "GEL": "🇬🇪",
66
- "VND": "🇻🇳",
67
- "AED": "🇦🇪",
68
- "AMD": "🇦🇲",
69
- "AZN": "🇦🇿",
70
- "CNY": "🇨🇳",
71
- "EUR": "🇪🇺",
72
- "HKD": "🇭🇰",
73
- "INR": "🇮🇳",
74
- "PHP": "🇵🇭",
75
- "USD": "🇺🇸",
76
- }
77
-
78
-
79
- async def fill_creds(person_id: int) -> tuple[dict[int, models.Cred], dict[int, list[int]]]:
80
- cq = models.Cred.filter(person_id=person_id)
81
- creds = {c.id: c for c in await cq}
82
- cur_creds = {
83
- pci: ids
84
- for pci, ids in await cq.annotate(ids=ArrayAgg("id")).group_by("pmcur_id").values_list("pmcur_id", "ids")
85
- }
86
- return creds, cur_creds
87
-
88
-
89
- async def fill_actors(person_id: int) -> tuple[dict[int, models.Actor], dict[int, list[int]]]:
90
- aq = models.Actor.filter(person_id=person_id)
91
- actors = {a.id: a for a in await aq}
92
- ex_act_id = {
93
- exi: ids[0]
94
- for exi, ids in await aq.annotate(ids=ArrayAgg("id")) # todo: check len(ids) == 1
95
- .group_by("ex_id")
96
- .values_list("ex_id", "ids")
97
- }
98
- return actors, ex_act_id
99
-
100
-
101
- async def edit(msg: Message, txt: str, rm: InlineKeyboardMarkup):
102
- await gather(msg.edit_text(txt), msg.edit_reply_markup(reply_markup=rm))
103
-
104
-
105
- async def ans(cbq: CallbackQuery, txt: str = None):
106
- await cbq.answer(txt, cache_time=99)
107
-
108
-
109
- async def dlt(msg: Message):
110
- await msg.delete()
111
-
112
-
113
- async def edt(msg: Message, txt: str, rm: InlineKeyboardMarkup):
114
- if msg.message_id == msg.bot.store.perm.msg_id:
115
- await msg.edit_text(txt, reply_markup=rm)
116
- else: # окно вызвано в ответ на текст, а не кнопку
117
- try:
118
- await msg.bot.edit_message_text(
119
- txt, chat_id=msg.chat.id, message_id=msg.bot.store.perm.msg_id, reply_markup=rm
120
- )
121
- except TelegramBadRequest as e:
122
- print(msg.bot.store.perm.msg_id, e)
File without changes