xync-bot 0.3.24.dev11__py3-none-any.whl → 0.3.25.dev1__py3-none-any.whl

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.

xync_bot/__main__.py ADDED
@@ -0,0 +1,36 @@
1
+ import logging
2
+ from asyncio import run
3
+
4
+ from PGram import Bot
5
+ from aiogram.client.default import DefaultBotProperties
6
+ from aiogram.enums import UpdateType
7
+ from x_model import init_db
8
+
9
+ from xync_bot.routers.main.handler import mr
10
+ from xync_bot.routers.cond import cr
11
+ from xync_bot.routers.pay.handler import pr
12
+ from xync_bot.routers import last
13
+ from xync_bot.store import Store
14
+
15
+ au = [
16
+ UpdateType.MESSAGE,
17
+ UpdateType.CALLBACK_QUERY,
18
+ UpdateType.CHAT_MEMBER,
19
+ UpdateType.MY_CHAT_MEMBER,
20
+ ] # , UpdateType.CHAT_JOIN_REQUEST
21
+ bot = Bot([cr, pr, mr, last], Store(), au, default=DefaultBotProperties(parse_mode="HTML"))
22
+
23
+ if __name__ == "__main__":
24
+ from xync_bot.loader import TOKEN, TORM
25
+
26
+ logging.basicConfig(level=logging.INFO)
27
+
28
+ async def main() -> None:
29
+ cn = await init_db(TORM)
30
+ bot.dp.workflow_data["store"].glob = await Store.Global() # todo: refact store loading
31
+ await bot.start(
32
+ TOKEN,
33
+ cn,
34
+ )
35
+
36
+ run(main())
@@ -0,0 +1,41 @@
1
+ import logging
2
+
3
+ from aiogram import Router, F
4
+ from aiogram.enums import ContentType
5
+ from aiogram.exceptions import TelegramBadRequest
6
+ from aiogram.types import Message
7
+
8
+ last = Router(name="last")
9
+
10
+
11
+ @last.message(
12
+ F.content_type.not_in(
13
+ {
14
+ ContentType.NEW_CHAT_MEMBERS,
15
+ # ContentType.LEFT_CHAT_MEMBER,
16
+ # ContentType.SUPERGROUP_CHAT_CREATED,
17
+ # ContentType.NEW_CHAT_PHOTO,
18
+ # ContentType.FORUM_TOPIC_CREATED,
19
+ # ContentType.FORUM_TOPIC_EDITED,
20
+ ContentType.FORUM_TOPIC_CLOSED,
21
+ # ContentType.GENERAL_FORUM_TOPIC_HIDDEN, # deletable
22
+ }
23
+ )
24
+ )
25
+ async def del_cbq(msg: Message):
26
+ try:
27
+ await msg.delete()
28
+ logging.info({"DELETED": msg.model_dump(exclude_none=True)})
29
+ except TelegramBadRequest:
30
+ logging.error({"NOT_DELETED": msg.model_dump(exclude_none=True)})
31
+
32
+
33
+ @last.message()
34
+ async def all_rest(msg: Message):
35
+ logging.warning(
36
+ {
37
+ "NO_HANDLED": msg.model_dump(
38
+ exclude_none=True,
39
+ )
40
+ }
41
+ )
@@ -8,7 +8,7 @@ from xync_schema.enums import SynonymType
8
8
 
9
9
  from xync_bot.routers.cond.func import wrap_cond, get_val, btns, rkm, ikm, SynTypeCd, CondCd
10
10
 
11
- cr = Router()
11
+ cr = Router(name="cond")
12
12
 
13
13
 
14
14
  @cr.message(Command("cond"))
@@ -89,9 +89,3 @@ async def got_action(cbq: CallbackQuery, callback_data: CondCd, state: FSMContex
89
89
  await (await state.get_value("cmsg")).delete()
90
90
  await show_cond(cbq.message, cond)
91
91
  return await cbq.answer(callback_data.act)
92
-
93
-
94
- @cr.message()
95
- async def unknown(msg: Message):
96
- # user = await User.get(username_id=msg.from_user.id)
97
- await msg.delete()
File without changes
@@ -1,8 +1,6 @@
1
1
  import logging
2
2
 
3
3
  from aiogram import Router, F
4
- from aiogram.enums import ContentType
5
- from aiogram.exceptions import TelegramBadRequest
6
4
  from aiogram.filters import CommandStart, CommandObject, ChatMemberUpdatedFilter, JOIN_TRANSITION, LEAVE_TRANSITION
7
5
  from aiogram.filters.callback_data import CallbackData
8
6
  from aiogram.types import (
@@ -18,8 +16,9 @@ from aiogram.utils.deep_linking import create_start_link
18
16
  from xync_schema import models
19
17
 
20
18
  from xync_bot.shared import NavCallbackData
19
+ from xync_bot.store import Store
21
20
 
22
- mr = Router()
21
+ mr = Router(name="main")
23
22
 
24
23
 
25
24
  class RrCallbackData(CallbackData, prefix="reg_res"): # registration response
@@ -30,6 +29,7 @@ class RrCallbackData(CallbackData, prefix="reg_res"): # registration response
30
29
  home_btns = InlineKeyboardMarkup(
31
30
  inline_keyboard=[
32
31
  [
32
+ InlineKeyboardButton(text="Transfer", callback_data=NavCallbackData(to="transfer").pack()),
33
33
  InlineKeyboardButton(text="Invite", callback_data=NavCallbackData(to="ref_link").pack()),
34
34
  InlineKeyboardButton(text="Get VPN", callback_data=NavCallbackData(to="get_vpn").pack()),
35
35
  ]
@@ -41,7 +41,7 @@ home_btns = InlineKeyboardMarkup(
41
41
  async def start_handler(msg: Message, command: CommandObject):
42
42
  me: User = msg.from_user
43
43
  ref_id: int = command.args.isnumeric() and int(command.args)
44
- user = await models.User.get_or_none(id=me.id, blocked=False)
44
+ user = await models.User.get(username_id=me.id, blocked=False)
45
45
  rm = None
46
46
  logging.info(msg, {"src": "start"})
47
47
  if user:
@@ -66,6 +66,27 @@ async def start_handler(msg: Message, command: CommandObject):
66
66
  return await msg.answer(rs, reply_markup=rm)
67
67
 
68
68
 
69
+ @mr.message(CommandStart(deep_link=True)) # attempt to reg by fake link
70
+ async def fraud_handler(msg: Message):
71
+ logging.warning(f"Start: {msg.from_user.id}. Msg: {msg}")
72
+ # todo: alert to admins! Fraud attempt!
73
+ await msg.answer("🤔")
74
+
75
+
76
+ @mr.message(CommandStart()) # обычный /start
77
+ async def home(msg: Message, store: Store):
78
+ me = msg.from_user
79
+ user, is_new = await models.User.tg_upsert(me, False)
80
+
81
+ rr = "сначала вы должны найти поручителя, и перейти по его реферальной ссылке.\nhttps://telegra.ph/XyncNet-02-13"
82
+ if is_new: # has ref and created now
83
+ await msg.answer(f"Здравствуйте {me.full_name}, что бы использовать возможности нашей сети, {rr}")
84
+ elif not user.ref_id:
85
+ await msg.answer(rr.capitalize())
86
+ else:
87
+ await msg.answer(f"{me.full_name}, не балуйтесь, вы и так уже активный участник👌🏼", reply_markup=home_btns)
88
+
89
+
69
90
  @mr.callback_query(RrCallbackData.filter())
70
91
  async def phrases_input_request(cb: CallbackQuery, callback_data: RrCallbackData) -> None:
71
92
  protege = await models.User[callback_data.to]
@@ -83,27 +104,7 @@ async def phrases_input_request(cb: CallbackQuery, callback_data: RrCallbackData
83
104
  await cb.message.edit_text(rs)
84
105
 
85
106
 
86
- @mr.message(CommandStart(deep_link=True)) # attempt to reg by fake link
87
- async def fraud_handler(msg: Message):
88
- logging.warning(f"Start: {msg.from_user.id}. Msg: {msg}")
89
- # todo: alert to admins! Fraud attempt!
90
- await msg.answer("🤔")
91
-
92
-
93
- @mr.message(CommandStart())
94
- async def start_no_ref_handler(msg: Message):
95
- me = msg.from_user
96
- user, cr = await models.User.tg2in(me, False)
97
- rr = "сначала вы должны найти поручителя, и перейти по его реферальной ссылке.\nhttps://telegra.ph/XyncNet-02-13"
98
- if cr: # has ref and created now
99
- await msg.answer(f"Здравствуйте {me.full_name}, что бы использовать возможности нашей сети, {rr}")
100
- elif not user.ref_id:
101
- await msg.answer(rr.capitalize())
102
- else:
103
- await msg.answer(f"{me.full_name}, не балуйтесь, вы и так уже активный участник👌🏼", reply_markup=home_btns)
104
-
105
-
106
- @mr.callback_query(NavCallbackData.filter(F.to == "ref_link"))
107
+ @mr.callback_query(NavCallbackData.filter(F.to.__eq__("ref_link")))
107
108
  async def ref_link_handler(cbq: CallbackQuery):
108
109
  me = cbq.from_user
109
110
  if not (u := await models.User.get_or_none(id=me.id, blocked=False).prefetch_related("ref")):
@@ -118,7 +119,7 @@ async def ref_link_handler(cbq: CallbackQuery):
118
119
  await cbq.answer("Wait for your protege request..")
119
120
 
120
121
 
121
- @mr.my_chat_member(F.chat.type == "private") # my_chat_member is fired on add bot to any chat. filter for preventing
122
+ @mr.my_chat_member(F.chat.type == "private") # my_chat_member is fired on adding bot to any chat. filter for preventing
122
123
  async def my_user_set_status(my_chat_member: ChatMemberUpdated):
123
124
  logging.info({"my_chat_member": my_chat_member.model_dump(exclude_none=True)})
124
125
  u: User = my_chat_member.from_user
@@ -128,7 +129,7 @@ async def my_user_set_status(my_chat_member: ChatMemberUpdated):
128
129
 
129
130
  @mr.my_chat_member()
130
131
  async def user_set_status(my_chat_member: ChatMemberUpdated):
131
- if my_chat_member.new_chat_member.user.username == "XyncNetBot": # удалена группа где бот был добавлен админом
132
+ if my_chat_member.new_chat_member.user.username == "XyncNetBot": # удалена группа, где бот был добавлен админом
132
133
  if forum := await models.Forum.get_or_none(id=my_chat_member.chat.id):
133
134
  await forum.delete()
134
135
  res = f"I {my_chat_member.new_chat_member.status} from {my_chat_member.chat.id}:{my_chat_member.chat.title}"
@@ -186,36 +187,3 @@ async def order_msg(msg: Message):
186
187
  await models.Msg.create(tgid=msg.message_id, txt=msg.text, order_id=order.id, receiver=receiver)
187
188
  logging.info(msg, {"src": "order_msg"})
188
189
  return await msg.send_copy(receiver.forum, message_thread_id=rcv_topic)
189
-
190
-
191
- @mr.message(
192
- F.content_type.not_in(
193
- {
194
- ContentType.NEW_CHAT_MEMBERS,
195
- # ContentType.LEFT_CHAT_MEMBER,
196
- # ContentType.SUPERGROUP_CHAT_CREATED,
197
- # ContentType.NEW_CHAT_PHOTO,
198
- # ContentType.FORUM_TOPIC_CREATED,
199
- # ContentType.FORUM_TOPIC_EDITED,
200
- ContentType.FORUM_TOPIC_CLOSED,
201
- # ContentType.GENERAL_FORUM_TOPIC_HIDDEN, # deletable
202
- }
203
- )
204
- )
205
- async def del_cbq(msg: Message):
206
- try:
207
- await msg.delete()
208
- logging.info({"DELETED": msg.model_dump(exclude_none=True)})
209
- except TelegramBadRequest:
210
- logging.error({"NOT_DELETED": msg.model_dump(exclude_none=True)})
211
-
212
-
213
- @mr.message()
214
- async def all_rest(msg: Message):
215
- logging.warning(
216
- {
217
- "NO_HANDLED": msg.model_dump(
218
- exclude_none=True,
219
- )
220
- }
221
- )
@@ -5,8 +5,6 @@ from aiogram.exceptions import TelegramBadRequest
5
5
  from aiogram.fsm.state import StatesGroup, State
6
6
  from aiogram.types import Message, InlineKeyboardMarkup
7
7
  from pyrogram.types import CallbackQuery
8
- from tortoise.functions import Min
9
- from x_auth.enums import Role
10
8
  from x_model.func import ArrayAgg
11
9
  from xync_schema import models
12
10
 
@@ -59,107 +57,6 @@ class PayStep(IntEnum):
59
57
  pending_receive = 19 # Ожидание поступления (если мы получаем фиат)
60
58
 
61
59
 
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
60
  async def fill_creds(person_id: int) -> tuple[dict[int, models.Cred], dict[int, list[int]]]:
164
61
  cq = models.Cred.filter(person_id=person_id)
165
62
  creds = {c.id: c for c in await cq}
@@ -206,39 +103,17 @@ async def edt(msg: Message, txt: str, rm: InlineKeyboardMarkup):
206
103
  print(msg.bot.store.perm.msg_id, e)
207
104
 
208
105
 
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
106
+ def fmt_sec(sec: int):
107
+ days = sec // (24 * 3600)
108
+ sec %= 24 * 3600
109
+ hours = sec // 3600
110
+ sec %= 3600
111
+ minutes = sec // 60
112
+ sec %= 60
213
113
 
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
- )
114
+ if days > 0:
115
+ return f"{days}д {hours:02d}:{minutes:02d}:{sec:02d}"
116
+ elif hours > 0:
117
+ return f"{hours:02d}:{minutes:02d}:{sec:02d}"
235
118
  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
- )
119
+ return f"{minutes:02d}:{sec:02d}"
@@ -1,89 +1,86 @@
1
1
  from asyncio import gather
2
2
  from datetime import datetime
3
3
 
4
- import PGram
5
4
  from aiogram import Router, F
6
5
  from aiogram.filters import Command
7
6
  from aiogram.types import Message, CallbackQuery
8
7
  from aiogram.fsm.context import FSMContext
9
- from xync_bot.routers.pay.dep import fill_creds, fill_actors, dlt, ans, Store
8
+ from xync_bot.routers.pay.dep import fill_creds, fill_actors, dlt, ans
10
9
  from xync_schema import models
11
10
 
12
11
  from xync_bot.routers.pay import cd, dep, window
12
+ from xync_bot.store import Store
13
13
 
14
- pay = Router()
14
+ pr = Router(name="pay")
15
15
 
16
16
 
17
- @pay.message(Command("pay"))
18
- async def h_start(msg: Message):
19
- """Step 1: Select target type"""
20
- store: Store = msg.bot.store
17
+ @pr.message(Command("pay"))
18
+ async def h_start(msg: Message, store: Store):
19
+ """Step 1: Select a target type"""
21
20
  store.curr.is_target = True
22
- await gather(window.type_select(msg), dlt(msg))
21
+ await gather(window.type_select(msg, store), dlt(msg))
23
22
  store.perm.user = await models.User.get(username_id=msg.from_user.id)
24
23
  store.perm.creds, store.perm.cur_creds = await fill_creds(store.perm.user.person_id)
24
+ store.perm.actors = await fill_actors(store.perm.user.person_id)
25
25
 
26
26
 
27
- @pay.callback_query(cd.MoneyType.filter(F.is_fiat))
28
- async def h_got_fiat_type(query: CallbackQuery, bot: PGram):
27
+ @pr.callback_query(cd.MoneyType.filter(F.is_fiat))
28
+ async def h_got_fiat_type(query: CallbackQuery, store: Store):
29
29
  """Step 2f: Select cur"""
30
- bot.store.curr.is_fiat = True
31
- await gather(window.cur_select(query.message), ans(query, "Понял, фиат"))
30
+ store.curr.is_fiat = True
31
+ await gather(window.cur_select(query.message, store), ans(query, "Понял, фиат"))
32
32
 
33
33
 
34
- @pay.callback_query(cd.MoneyType.filter(F.is_fiat.__eq__(0)))
35
- async def h_got_crypto_type(query: CallbackQuery, bot: PGram):
34
+ @pr.callback_query(cd.MoneyType.filter(F.is_fiat.__eq__(0)))
35
+ async def h_got_crypto_type(query: CallbackQuery, store: Store):
36
36
  """Step 2c: Select coin"""
37
- bot.store.curr.is_fiat = False
38
- bot.store.perm.actors, *_ = await gather(
39
- fill_actors(bot.store.perm.user.person_id), window.coin_select(query.message), ans(query, "Понял, крипта")
40
- )
37
+ store.curr.is_fiat = False
38
+ await gather(window.coin_select(query.message, store), ans(query, "Понял, крипта"))
41
39
 
42
40
 
43
- @pay.callback_query(cd.Coin.filter())
44
- async def h_got_coin(query: CallbackQuery, callback_data: cd.Coin, bot: PGram):
41
+ @pr.callback_query(cd.Coin.filter())
42
+ async def h_got_coin(query: CallbackQuery, callback_data: cd.Coin, store: Store):
45
43
  """Step 3c: Select target ex"""
46
- setattr(bot.store.pay, ("t" if bot.store.curr.is_target else "s") + "_coin_id", callback_data.id)
47
- await gather(window.ex_select(query.message), ans(query, "Эта монета есть на следующих биржах"))
44
+ setattr(store.pay, ("t" if store.curr.is_target else "s") + "_coin_id", callback_data.id)
45
+ await gather(window.ex_select(query.message, store), ans(query, "Эта монета есть на следующих биржах"))
48
46
 
49
47
 
50
- @pay.callback_query(cd.Cur.filter())
51
- async def h_got_cur(query: CallbackQuery, callback_data: cd.Cur, bot: PGram):
48
+ @pr.callback_query(cd.Cur.filter())
49
+ async def h_got_cur(query: CallbackQuery, callback_data: cd.Cur, store: Store):
52
50
  """Step 3f: Select target pm"""
53
- setattr(bot.store.pay, ("t" if bot.store.curr.is_target else "s") + "_cur_id", callback_data.id)
54
- await gather(window.pm(query.message), ans(query, "Вот платежные системы доступные для этой валюты"))
51
+ setattr(store.pay, ("t" if store.curr.is_target else "s") + "_cur_id", callback_data.id)
52
+ await gather(window.pm(query.message, store), ans(query, "Вот платежные системы доступные для этой валюты"))
55
53
 
56
54
 
57
- @pay.callback_query(cd.Pm.filter(F.is_target))
58
- async def h_got_target_pm(query: CallbackQuery, callback_data: cd.Pm, state: FSMContext):
55
+ @pr.callback_query(cd.Pm.filter(F.is_target))
56
+ async def h_got_target_pm(query: CallbackQuery, callback_data: cd.Pm, state: FSMContext, store: Store):
59
57
  """Step 4f: Fill target cred.detail"""
60
- query.message.bot.store.pay.t_pmcur_id = callback_data.pmcur_id
58
+ store.pay.t_pmcur_id = callback_data.pmcur_id
61
59
  await gather(
62
- window.fill_cred_dtl(query.message),
60
+ window.fill_cred_dtl(query.message, store),
63
61
  ans(query, "Теперь нужны реквизиты"),
64
62
  state.set_state(dep.CredState.detail),
65
63
  )
66
64
 
67
65
 
68
- @pay.callback_query(cd.Cred.filter())
69
- async def h_got_cred(query: CallbackQuery, callback_data: cd.Cred, state: FSMContext):
70
- query.message.bot.store.pay.cred_id = callback_data.id
66
+ @pr.callback_query(cd.Cred.filter())
67
+ async def h_got_cred(query: CallbackQuery, callback_data: cd.Cred, state: FSMContext, store: Store):
68
+ store.pay.cred_id = callback_data.id
71
69
  await gather(
72
- window.amount(query.message), ans(query, "Теперь нужна сумма"), state.set_state(dep.PaymentState.amount)
70
+ window.amount(query.message, store), ans(query, "Теперь нужна сумма"), state.set_state(dep.PaymentState.amount)
73
71
  )
74
72
 
75
73
 
76
- @pay.message(dep.CredState.detail)
77
- async def h_got_cred_dtl(msg: Message, state: FSMContext):
74
+ @pr.message(dep.CredState.detail)
75
+ async def h_got_cred_dtl(msg: Message, state: FSMContext, store: Store):
78
76
  """Step 4.1f: Fill target cred.name"""
79
- msg.bot.store.pay.cred_dtl = msg.text
80
- await gather(window.fill_cred_name(msg), dlt(msg), state.set_state(dep.CredState.name))
77
+ store.pay.cred_dtl = msg.text
78
+ await gather(window.fill_cred_name(msg, store), dlt(msg), state.set_state(dep.CredState.name))
81
79
 
82
80
 
83
- @pay.message(dep.CredState.name)
84
- async def h_got_cred_name(msg: Message, state: FSMContext):
81
+ @pr.message(dep.CredState.name)
82
+ async def h_got_cred_name(msg: Message, state: FSMContext, store: Store):
85
83
  """Step 5f: Save target cred"""
86
- store: Store = msg.bot.store
87
84
  cred, _ = await models.Cred.update_or_create(
88
85
  {"name": msg.text},
89
86
  detail=store.pay.cred_dtl,
@@ -92,30 +89,28 @@ async def h_got_cred_name(msg: Message, state: FSMContext):
92
89
  )
93
90
  store.pay.cred_id = cred.id
94
91
  store.perm.creds[cred.id] = cred
95
- await gather(window.amount(msg), dlt(msg), state.set_state(dep.PaymentState.amount))
92
+ await gather(window.amount(msg, store), dlt(msg), state.set_state(dep.PaymentState.amount))
96
93
 
97
94
 
98
- @pay.callback_query(cd.Ex.filter())
99
- async def h_got_ex(query: CallbackQuery, callback_data: cd.Ex, state: FSMContext):
95
+ @pr.callback_query(cd.Ex.filter())
96
+ async def h_got_ex(query: CallbackQuery, callback_data: cd.Ex, state: FSMContext, store: Store):
100
97
  """Step 4c: Save target"""
101
- store: Store = query.message.bot.store
102
98
  ist = store.curr.is_target
103
99
  setattr(store.pay, ("t" if ist else "s") + "_ex_id", callback_data.id)
104
100
  if ist:
105
- await window.amount(query.message)
101
+ await window.amount(query.message, store)
106
102
  actor_id = store.perm.actors[store.pay.t_ex_id]
107
103
  addr = await models.Addr.get(coin_id=store.pay.t_coin_id, actor_id=actor_id)
108
104
  store.pay.addr_id = addr.id
109
105
  else:
110
- await window.set_ppo(query.message)
106
+ await window.set_ppo(query.message, store)
111
107
  await ans(query, f"Биржа {store.glob.exs[callback_data.id]} выбрана")
112
108
  await state.set_state(dep.PaymentState.amount)
113
109
 
114
110
 
115
- @pay.message(dep.PaymentState.amount)
116
- async def h_got_amount(msg: Message, state: FSMContext):
117
- """Step 6: Save target amount"""
118
- store: Store = msg.bot.store
111
+ @pr.message(dep.PaymentState.amount)
112
+ async def h_got_amount(msg: Message, state: FSMContext, store: Store):
113
+ """Step 6: Save a target amount"""
119
114
  if not msg.text.isnumeric():
120
115
  store.curr.msg_to_del = await msg.answer("Пожалуйста, введите корректное число")
121
116
  return
@@ -124,34 +119,33 @@ async def h_got_amount(msg: Message, state: FSMContext):
124
119
  store.pay.amount = float(msg.text)
125
120
  """Step 7: Select source type"""
126
121
  store.curr.is_target = False
127
- await gather((window.type_select if store.curr.is_fiat else window.cur_select)(msg), dlt(msg), state.clear())
122
+ await gather((window.type_select if store.curr.is_fiat else window.cur_select)(msg, store), dlt(msg), state.clear())
128
123
 
129
124
 
130
- @pay.callback_query(cd.Pm.filter(F.is_target.__eq__(0)))
131
- async def h_got_source_pm(query: CallbackQuery, callback_data: cd.Pm):
132
- store: Store = query.message.bot.store
125
+ @pr.callback_query(cd.Pm.filter(F.is_target.__eq__(0)))
126
+ async def h_got_source_pm(query: CallbackQuery, callback_data: cd.Pm, store: Store):
133
127
  store.pay.s_pmcur_id = callback_data.pmcur_id
134
128
  await gather(
135
- window.set_ppo(query.message),
129
+ window.set_ppo(query.message, store),
136
130
  ans(query, store.glob.pmcurs[callback_data.pmcur_id]),
137
131
  )
138
132
 
139
133
 
140
- @pay.callback_query(cd.Ppo.filter())
141
- async def h_got_ppo(query: CallbackQuery, callback_data: cd.Ppo):
142
- query.message.bot.store.pay.ppo = callback_data.num
143
- await gather(window.set_urgency(query.message), ans(query, str(callback_data.num)))
134
+ @pr.callback_query(cd.Ppo.filter())
135
+ async def h_got_ppo(query: CallbackQuery, callback_data: cd.Ppo, store: Store):
136
+ store.pay.ppo = callback_data.num
137
+ await gather(window.set_urgency(query.message, store), ans(query, str(callback_data.num)))
144
138
 
145
139
 
146
- @pay.callback_query(cd.Time.filter())
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)
140
+ @pr.callback_query(cd.Time.filter())
141
+ async def h_got_urgency(query: CallbackQuery, callback_data: cd.Time, store: Store):
142
+ store.pay.urg = callback_data.minutes
143
+ await window.create_payreq(query.message, store)
150
144
  await ans(query, f"Ok {callback_data.minutes} min.")
151
145
 
152
146
 
153
147
  # ACTIONS
154
- @pay.callback_query(cd.Action.filter(F.act.__eq__(cd.ActionType.received)))
148
+ @pr.callback_query(cd.Action.filter(F.act.__eq__(cd.ActionType.received)))
155
149
  async def payment_confirmed(query: CallbackQuery, state: FSMContext):
156
150
  await ans(query, None)
157
151
  payed_at = datetime.now()
@@ -165,7 +159,7 @@ async def payment_confirmed(query: CallbackQuery, state: FSMContext):
165
159
  await window.success(query.message)
166
160
 
167
161
 
168
- @pay.callback_query(cd.Action.filter(F.act.__eq__(cd.ActionType.not_received)))
162
+ @pr.callback_query(cd.Action.filter(F.act.__eq__(cd.ActionType.not_received)))
169
163
  async def no_payment(query: CallbackQuery, state: FSMContext):
170
164
  await ans(query, None)
171
165
  await state.update_data(timer_active=False)
@@ -175,80 +169,80 @@ async def no_payment(query: CallbackQuery, state: FSMContext):
175
169
  await state.set_state(dep.Report.text)
176
170
 
177
171
 
178
- @pay.message(dep.Report.text)
172
+ @pr.message(dep.Report.text)
179
173
  async def payment_not_specified(msg: Message, state: FSMContext):
180
174
  await state.update_data(text=msg.text)
181
175
  data = await state.get_data()
182
176
  complaint_text = (
183
177
  f"Жалоба на неполученный платеж:\n"
184
178
  f"Пользователь: @{msg.from_user.username or msg.from_user.id}\n"
185
- f"Детали платежа: {data["text"]}\n"
179
+ f"Детали платежа: {data['text']}\n"
186
180
  f"Время: {msg.date.strftime('%Y-%m-%d %H:%M:%S')}"
187
181
  )
188
182
  await msg.bot.send_message(chat_id="xyncpay", text=complaint_text)
189
183
 
190
184
 
191
185
  # NAVIGATION
192
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_type, cd.PayStep.s_type])))
193
- async def handle_home(query: CallbackQuery, state: FSMContext):
194
- await gather(window.type_select(query.message), state.clear(), ans(query, "Создаем платеж заново"))
186
+ @pr.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_type, cd.PayStep.s_type])))
187
+ async def handle_home(query: CallbackQuery, state: FSMContext, store: Store):
188
+ await gather(window.type_select(query.message, store), state.clear(), ans(query, "Создаем платеж заново"))
195
189
 
196
190
 
197
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_coin, cd.PayStep.s_coin])))
198
- async def to_coin_select(query: CallbackQuery, state: FSMContext):
191
+ @pr.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_coin, cd.PayStep.s_coin])))
192
+ async def to_coin_select(query: CallbackQuery, state: FSMContext, store: Store):
199
193
  await ans(query, None)
200
194
  is_target = await state.get_value("is_target")
201
195
  pref = "t" if is_target else "s"
202
196
  await state.update_data({pref + "_ex_id": None, pref + "_coin_id": None})
203
- await window.coin_select(query.message)
197
+ await window.coin_select(query.message, store)
204
198
 
205
199
 
206
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_cur, cd.PayStep.s_cur])))
207
- async def to_cur_select(query: CallbackQuery, state: FSMContext):
200
+ @pr.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_cur, cd.PayStep.s_cur])))
201
+ async def to_cur_select(query: CallbackQuery, state: FSMContext, store: Store):
208
202
  await ans(query, None)
209
203
  is_target = await state.get_value("is_target")
210
204
  pref = "t" if is_target else "s"
211
205
  await state.update_data({pref + "_pmcur_id": None, pref + "_cur_id": None})
212
- await window.cur_select(query.message)
206
+ await window.cur_select(query.message, store)
213
207
 
214
208
 
215
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_pm, cd.PayStep.s_pm])))
216
- async def to_pm_select(query: CallbackQuery, state: FSMContext):
209
+ @pr.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_pm, cd.PayStep.s_pm])))
210
+ async def to_pm_select(query: CallbackQuery, store: Store):
217
211
  await ans(query, None)
218
- await window.pm(query.message)
212
+ await window.pm(query.message, store)
219
213
 
220
214
 
221
- @pay.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_cred_dtl)))
222
- async def back_to_cred_detail(query: CallbackQuery, state: FSMContext):
215
+ @pr.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_cred_dtl)))
216
+ async def back_to_cred_detail(query: CallbackQuery, state: FSMContext, store: Store):
223
217
  await ans(query, None)
224
218
  await state.update_data(detail=None)
225
- await window.fill_cred_dtl(query.message)
219
+ await window.fill_cred_dtl(query.message, store)
226
220
 
227
221
 
228
- @pay.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_cred_name)))
229
- async def back_to_cred_name(query: CallbackQuery, state: FSMContext):
222
+ @pr.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_cred_name)))
223
+ async def back_to_cred_name(query: CallbackQuery, state: FSMContext, store: Store):
230
224
  await ans(query, None)
231
225
  await state.update_data(name=None)
232
- await window.fill_cred_name(query.message)
226
+ await window.fill_cred_name(query.message, store)
233
227
 
234
228
 
235
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_ex, cd.PayStep.s_ex])))
236
- async def back_to_ex_select(query: CallbackQuery, state: FSMContext):
229
+ @pr.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_ex, cd.PayStep.s_ex])))
230
+ async def back_to_ex_select(query: CallbackQuery, state: FSMContext, store: Store):
237
231
  await ans(query, None)
238
232
  await state.update_data({("t" if await state.get_value("is_target") else "s") + "ex_id": None})
239
- await window.ex_select(query.message)
233
+ await window.ex_select(query.message, store)
240
234
 
241
235
 
242
- @pay.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_amount)))
243
- async def back_to_amount(query: CallbackQuery, state: FSMContext):
236
+ @pr.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_amount)))
237
+ async def back_to_amount(query: CallbackQuery, state: FSMContext, store: Store):
244
238
  await ans(query, None)
245
239
  await state.update_data(amount=None)
246
- await window.amount(query.message)
240
+ await window.amount(query.message, store)
247
241
  await state.set_state(dep.PaymentState.amount)
248
242
 
249
243
 
250
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_pm])))
251
- async def back_to_payment(query: CallbackQuery, state: FSMContext):
244
+ @pr.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_pm])))
245
+ async def back_to_payment(query: CallbackQuery, state: FSMContext, store: Store):
252
246
  await ans(query, None)
253
247
  await state.update_data(payment=None)
254
- await window.pm(query.message)
248
+ await window.pm(query.message, store)
@@ -6,14 +6,13 @@ from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
6
6
  from aiogram.utils.keyboard import InlineKeyboardBuilder
7
7
  from xync_schema import models
8
8
 
9
- from xync_bot.routers.pay.dep import edt, need_ppo, have_coin_amount, get_in, Store
10
-
9
+ from xync_bot.routers.pay.dep import edt, fmt_sec
11
10
  from xync_bot.routers.pay import cd, dep
11
+ from xync_bot.store import Store
12
12
 
13
13
 
14
- async def type_select(msg: Message):
14
+ async def type_select(msg: Message, store: Store):
15
15
  """Step 1: Select type"""
16
- store: Store = msg.bot.store
17
16
  ist: bool = store.curr.is_target
18
17
  rm = InlineKeyboardMarkup(
19
18
  inline_keyboard=[
@@ -44,11 +43,11 @@ async def type_select(msg: Message):
44
43
  store.perm.msg_id = msg.message_id
45
44
 
46
45
 
47
- async def cur_select(msg: Message):
46
+ async def cur_select(msg: Message, store: Store):
48
47
  """Common using cur func"""
49
48
  builder = InlineKeyboardBuilder()
50
- ist: bool = msg.bot.store.curr.is_target
51
- for cur_id, cur in msg.bot.store.glob.curs.items():
49
+ ist: bool = store.curr.is_target
50
+ for cur_id, cur in store.glob.curs.items():
52
51
  builder.button(text=cur.ticker + dep.flags[cur.ticker], callback_data=cd.Cur(id=cur_id, is_target=ist))
53
52
  builder.button(text="Назад к выбору типа", callback_data=cd.PayNav(to=cd.PayStep.t_type))
54
53
  builder.adjust(3, 3, 3, 3, 3, 1)
@@ -56,10 +55,9 @@ async def cur_select(msg: Message):
56
55
  await edt(msg, "Выбери валюту котор" + sfx, builder.as_markup())
57
56
 
58
57
 
59
- async def coin_select(msg: Message):
58
+ async def coin_select(msg: Message, store: Store):
60
59
  """Common using coin func"""
61
60
  builder = InlineKeyboardBuilder()
62
- store: Store = msg.bot.store
63
61
  for coin_id, ticker in store.glob.coins.items():
64
62
  builder.button(text=ticker, callback_data=cd.Coin(id=coin_id, is_target=store.curr.is_target))
65
63
  builder.button(
@@ -71,8 +69,7 @@ async def coin_select(msg: Message):
71
69
  await msg.edit_text("Выберите монету котор" + sfx, reply_markup=builder.as_markup())
72
70
 
73
71
 
74
- async def ex_select(msg: Message):
75
- store: Store = msg.bot.store
72
+ async def ex_select(msg: Message, store: Store):
76
73
  ist = store.curr.is_target
77
74
  coin_id = getattr(store.pay, ("t" if ist else "s") + "_coin_id")
78
75
  builder = InlineKeyboardBuilder()
@@ -87,8 +84,7 @@ async def ex_select(msg: Message):
87
84
  await msg.edit_text("На какую биржу?" if ist else "С какой биржи?", reply_markup=keyboard)
88
85
 
89
86
 
90
- async def pm(msg: Message):
91
- store: Store = msg.bot.store
87
+ async def pm(msg: Message, store: Store):
92
88
  ist = store.curr.is_target
93
89
  cur_id = getattr(store.pay, ("t" if ist else "s") + "_cur_id")
94
90
  builder = InlineKeyboardBuilder()
@@ -103,9 +99,8 @@ async def pm(msg: Message):
103
99
  await msg.edit_text("На какую платежную систему?" if ist else "C какой платежной системы?", reply_markup=keyboard)
104
100
 
105
101
 
106
- async def fill_cred_dtl(msg: Message):
102
+ async def fill_cred_dtl(msg: Message, store: Store):
107
103
  builder = InlineKeyboardBuilder()
108
- store: Store = msg.bot.store
109
104
  txt = "В"
110
105
  if cred_ids := store.perm.cur_creds.get(store.pay.t_pmcur_id):
111
106
  for cred_id in cred_ids:
@@ -122,22 +117,20 @@ async def fill_cred_dtl(msg: Message):
122
117
  )
123
118
 
124
119
 
125
- async def fill_cred_name(msg: Message):
120
+ async def fill_cred_name(msg: Message, store: Store):
126
121
  builder = InlineKeyboardBuilder()
127
122
  builder.button(text="Назад к вводу реквизитов", callback_data=cd.PayNav(to=cd.PayStep.t_cred_dtl))
128
123
  builder.button(text="Домой", callback_data=cd.PayNav(to=cd.PayStep.t_type))
129
124
  builder.adjust(2)
130
- store: Store = msg.bot.store
131
125
  cur = store.glob.curs[store.pay.t_cur_id]
132
126
  payment = store.glob.pmcurs[store.pay.t_pmcur_id]
133
127
  detail = store.pay.cred_dtl
134
128
  await edt(msg, f"{cur.ticker}:{payment}:{detail}: Введите имя получателя", builder.as_markup())
135
129
 
136
130
 
137
- async def amount(msg: Message):
131
+ async def amount(msg: Message, store: Store):
138
132
  """Step 5: Filling target amount"""
139
133
  builder = InlineKeyboardBuilder()
140
- store: Store = msg.bot.store
141
134
  if store.curr.is_fiat:
142
135
  cur_coin = store.glob.curs[store.pay.t_cur_id].ticker
143
136
  builder.button(text="Назад к вводу имени", callback_data=cd.PayNav(to=cd.PayStep.t_cred_name))
@@ -153,10 +146,9 @@ async def amount(msg: Message):
153
146
  await edt(msg, f"Введите нужную сумму {cur_coin} для {t_name}", builder.as_markup())
154
147
 
155
148
 
156
- async def set_ppo(msg: Message):
157
- store: Store = msg.bot.store
149
+ async def set_ppo(msg: Message, store: Store):
158
150
  ist = store.curr.is_target
159
- if nppo := await need_ppo(store):
151
+ if nppo := await store.need_ppo():
160
152
  builder = InlineKeyboardBuilder()
161
153
  builder.button(text="Нет", callback_data=cd.Ppo(num=1, is_target=ist)).button(
162
154
  text="Да", callback_data=cd.Ppo(num=2, is_target=ist)
@@ -172,9 +164,8 @@ async def set_ppo(msg: Message):
172
164
  await set_urgency(msg)
173
165
 
174
166
 
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):
167
+ async def set_urgency(msg: Message, store: Store):
168
+ if not store.curr.is_fiat or await store.client_have_coin_amount():
178
169
  return await create_payreq(msg) # next
179
170
  builder = InlineKeyboardBuilder()
180
171
  (
@@ -183,15 +174,14 @@ async def set_urgency(msg: Message):
183
174
  .button(text="30 мин", callback_data=cd.Time(minutes=30))
184
175
  .button(text="3 часа", callback_data=cd.Time(minutes=180))
185
176
  .button(text="сутки", callback_data=cd.Time(minutes=60 * 24))
186
- .button(text="Назад к вводу платежей", callback_data=cd.PayNav(to=cd.PayStep.t_pm))
177
+ .button(text="Назад к выбору платежной\nсистемы для отправки", callback_data=cd.PayNav(to=cd.PayStep.s_pm))
187
178
  .button(text="Домой", callback_data=cd.PayNav(to=cd.PayStep.t_type))
188
179
  .adjust(2, 2, 1, 1, 1)
189
180
  )
190
181
  return await edt(msg, "Сколько можешь ждать?", builder.as_markup())
191
182
 
192
183
 
193
- async def create_payreq(msg: Message):
194
- store: Store = msg.bot.store
184
+ async def create_payreq(msg: Message, store: Store):
195
185
  pay_req, _ = await models.PayReq.update_or_create(
196
186
  {"pay_until": datetime.now() + timedelta(minutes=store.pay.urg)},
197
187
  amount=store.pay.amount,
@@ -201,41 +191,29 @@ async def create_payreq(msg: Message):
201
191
  user=store.perm.user,
202
192
  )
203
193
  store.pay.pr_id = pay_req.id
204
- inp, txt = await get_in(store)
194
+ inp, txt = await store.get_merch_target()
195
+ ccred, ctxt = await store.client_target_repr()
196
+ txt += f"\nИ получите {store.pay.amount} {ctxt} в течение {fmt_sec(store.pay.urg * 60)}"
197
+ if store.pay.ppo > 1:
198
+ txt += f" максимум {store.pay.ppo} платежами"
205
199
  rm = InlineKeyboardMarkup(
206
200
  inline_keyboard=[
207
201
  [InlineKeyboardButton(text="Отправил", callback_data=cd.Action(act=cd.ActionType.sent).pack())],
208
202
  ]
209
203
  )
210
- await edt(msg, f"Отправь {store.pay.amount}" + txt, rm.as_markup())
204
+ await edt(msg, f"Отправьте {100500} " + txt, rm) # todo: get rate
211
205
 
212
- # create_task(window.run_timer(msg))
206
+ # create_task(window.run_timer(msg))В
213
207
 
214
208
 
215
209
  async def run_timer(message, state: FSMContext):
216
210
  builder = InlineKeyboardBuilder()
217
211
  builder.button(text="Платеж получен", callback_data=cd.Action(act=cd.ActionType.received))
218
212
 
219
- data = await state.get_value("timer")
220
- seconds = data * 60
221
-
222
- def format(sec):
223
- days = sec // (24 * 3600)
224
- sec %= 24 * 3600
225
- hours = sec // 3600
226
- sec %= 3600
227
- minutes = sec // 60
228
- sec %= 60
229
-
230
- if days > 0:
231
- return f"{days}д {hours:02d}:{minutes:02d}:{sec:02d}"
232
- elif hours > 0:
233
- return f"{hours:02d}:{minutes:02d}:{sec:02d}"
234
- else:
235
- return f"{minutes:02d}:{sec:02d}"
213
+ seconds = await state.get_value("timer") * 60
236
214
 
237
215
  try:
238
- await message.edit_text(f"⏳ Осталось {format(seconds)}", reply_markup=builder.as_markup())
216
+ await message.edit_text(f"⏳ Осталось {fmt_sec(seconds)}", reply_markup=builder.as_markup())
239
217
  except Exception:
240
218
  return
241
219
 
@@ -243,7 +221,7 @@ async def run_timer(message, state: FSMContext):
243
221
  await sleep(1)
244
222
  seconds -= 1
245
223
  try:
246
- await message.edit_text(f"⏳ Осталось {format(seconds)}", reply_markup=builder.as_markup())
224
+ await message.edit_text(f"⏳ Осталось {fmt_sec(seconds)}", reply_markup=builder.as_markup())
247
225
  await state.update_data(timer=seconds)
248
226
  except Exception:
249
227
  break
xync_bot/shared.py CHANGED
@@ -3,3 +3,22 @@ from aiogram.filters.callback_data import CallbackData
3
3
 
4
4
  class NavCallbackData(CallbackData, prefix="nav"): # navigate menu
5
5
  to: str
6
+
7
+
8
+ flags = {
9
+ "RUB": "🇷🇺",
10
+ "THB": "🇹🇭",
11
+ "IDR": "🇮🇩",
12
+ "TRY": "🇹🇷",
13
+ "GEL": "🇬🇪",
14
+ "VND": "🇻🇳",
15
+ "AED": "🇦🇪",
16
+ "AMD": "🇦🇲",
17
+ "AZN": "🇦🇿",
18
+ "CNY": "🇨🇳",
19
+ "EUR": "🇪🇺",
20
+ "HKD": "🇭🇰",
21
+ "INR": "🇮🇳",
22
+ "PHP": "🇵🇭",
23
+ "USD": "🇺🇸",
24
+ }
xync_bot/store.py ADDED
@@ -0,0 +1,157 @@
1
+ import logging
2
+ from aiogram.types import Message
3
+ from tortoise.functions import Min
4
+ from x_model.func import ArrayAgg
5
+ from x_auth.enums import Role
6
+ from xync_schema.models import Addr, Asset, Cred, Coin, Pmcur, Cur, User, Ex, Pmex
7
+
8
+ from xync_bot.shared import flags
9
+
10
+
11
+ class SingleStore(type):
12
+ _store = None
13
+
14
+ async def __call__(cls):
15
+ if not cls._store:
16
+ cls._store = super(SingleStore, cls).__call__()
17
+ cls._store.coins = {k: v for k, v in await Coin.all().order_by("ticker").values_list("id", "ticker")}
18
+ curs = {c.id: c for c in await Cur.filter(ticker__in=flags.keys()).order_by("ticker")}
19
+ cls._store.curs = curs
20
+ cls._store.exs = {k: v for k, v in await Ex.all().values_list("id", "name")}
21
+ cls._store.pmcurs = {
22
+ k: v
23
+ for k, v in await Pmex.filter(pm__pmcurs__cur_id__in=cls._store.curs.keys())
24
+ .annotate(sname=Min("name"))
25
+ .group_by("pm__pmcurs__id")
26
+ .values_list("pm__pmcurs__id", "sname")
27
+ }
28
+ cls._store.coinexs = {
29
+ c.id: [ex.ex_id for ex in c.coinexs] for c in await Coin.all().prefetch_related("coinexs")
30
+ }
31
+ cls._store.curpms = {
32
+ cur_id: ids
33
+ for cur_id, ids in await Pmcur.filter(cur_id__in=curs.keys())
34
+ .annotate(ids=ArrayAgg("id"))
35
+ .group_by("cur_id")
36
+ .values_list("cur_id", "ids")
37
+ }
38
+ cls._store.curpms = {
39
+ cur_id: ids
40
+ for cur_id, ids in await Pmcur.filter(cur_id__in=curs.keys())
41
+ .annotate(ids=ArrayAgg("id"))
42
+ .group_by("cur_id")
43
+ .values_list("cur_id", "ids")
44
+ }
45
+
46
+ return cls._store
47
+
48
+
49
+ class Store:
50
+ class Global(metaclass=SingleStore):
51
+ coins: dict[int, str] # id:ticker
52
+ curs: dict[int, Cur] # id:Cur
53
+ exs: dict[int, str] # id:name
54
+ coinexs: dict[int, list[int]] # id:[ex_ids]
55
+ pmcurs: dict[int, str] # pmcur_id:name
56
+ curpms: dict[int, list[int]] # id:[pmcur_ids]
57
+
58
+ class Personal:
59
+ class Current:
60
+ is_target: bool = True
61
+ is_fiat: bool = None
62
+ msg_to_del: Message = None
63
+
64
+ msg_id: int = None
65
+ user: User = None
66
+ actors: dict[int, int] = None # key=ex_id
67
+ creds: dict[int, Cred] = None # key=cred_id
68
+ cur_creds: dict[int, list[int]] = None # pmcur_id:[cred_ids]
69
+
70
+ def __init__(self, order_id: int):
71
+ self.order_id = order_id
72
+
73
+ curr: Current = Current()
74
+
75
+ class PayReq:
76
+ t_cur_id: int = None
77
+ s_cur_id: int = None
78
+ t_coin_id: int = None
79
+ s_coin_id: int = None
80
+ t_pmcur_id: int = None
81
+ s_pmcur_id: int = None
82
+ t_ex_id: int = None
83
+ s_ex_id: int = None
84
+ amount: int | float = None
85
+ ppo: int = 1
86
+ addr_id: int = None
87
+ cred_dtl: str = None
88
+ cred_id: int = None
89
+ urg: int = 5
90
+ pr_id: int = None
91
+
92
+ def __init__(self, uid: int):
93
+ self.uid = uid
94
+
95
+ async def xync_have_coin_amount(self) -> bool:
96
+ assets = await Asset.filter(
97
+ addr__coin_id=self.t_coin_id, addr__ex_id=self.t_ex_id, addr__actor__user__role__in=Role.ADMIN
98
+ )
99
+ return self.amount <= sum(a.free for a in assets)
100
+
101
+ async def client_have_coin_amount(self) -> bool:
102
+ assets = await Asset.filter(addr__coin_id=self.t_coin_id, addr__actor_id__in=self.perm.actors.values())
103
+ return self.amount <= sum(a.free for a in assets)
104
+
105
+ async def need_ppo(self):
106
+ cur_id = getattr(self, ("t" if self.curr.is_target else "s") + "_cur_id")
107
+ usd_amount = self.amount * self.glob.curs[cur_id].rate
108
+ if usd_amount < 50:
109
+ return 0
110
+ elif usd_amount > 100:
111
+ return 2
112
+ else:
113
+ return 1
114
+
115
+ async def client_target_repr(self) -> tuple[Addr | Cred, str]:
116
+ if self.t_ex_id:
117
+ addr_to = (
118
+ await Addr.filter(actor__ex_id=self.t_ex_id, coin_id=self.t_coin_id, actor__user=self.perm.user)
119
+ .prefetch_related("actor")
120
+ .first()
121
+ )
122
+ ex, coin = self.glob.exs[self.s_ex_id], self.glob.coins[self.s_coin_id]
123
+ if not addr_to:
124
+ logging.error(f"No {coin} addr in {ex} for user: {self.perm.user.username_id}")
125
+ return addr_to, f"{coin} на {ex} по id: `{addr_to.actor.exid}`"
126
+ # иначе: реквизиты для фиата
127
+ cur, pm = self.glob.curs[self.t_cur_id], self.glob.pmcurs[self.t_pmcur_id]
128
+ cred = self.perm.creds[self.cred_id]
129
+ return cred, f"{cur.ticker} на {pm} по номеру: {cred.repr()}"
130
+
131
+ async def get_merch_target(self) -> tuple[Addr | Cred, str]:
132
+ if self.s_ex_id:
133
+ addr_in = (
134
+ await Addr.filter(
135
+ actor__ex_id=self.s_ex_id, coin_id=self.s_coin_id, actor__user__role__gte=Role.ADMIN
136
+ )
137
+ .prefetch_related("actor")
138
+ .first()
139
+ )
140
+ ex, coin = self.glob.exs[self.s_ex_id], self.glob.coins[self.s_coin_id]
141
+ if not addr_in:
142
+ logging.error(f"No {coin} addr in {ex}")
143
+ return addr_in, f"{coin} на {ex} по id: `{addr_in.actor.exid}`"
144
+ # иначе: реквизиты для фиатной оплаты
145
+ s_pmcur = await Pmcur.get(id=self.s_pmcur_id).prefetch_related("pm__grp")
146
+ cred = await Cred.filter(
147
+ **({"pmcur__pm__grp": s_pmcur.pm.grp} if s_pmcur.pm.grp else {"pmcur_id": self.s_pmcur_id}),
148
+ person__user__role__gte=Role.ADMIN,
149
+ ).first() # todo: payreq by fiat.target-fiat.amount
150
+ cur, pm = self.glob.curs[self.s_cur_id], self.glob.pmcurs[self.s_pmcur_id]
151
+ if not cred:
152
+ logging.error(f"No {cur.ticker} cred for {pm}")
153
+ return cred, f"{cur.ticker} на {pm} по номеру: {cred.repr()}"
154
+
155
+ glob: Global
156
+ pers: dict[int, Personal] = {}
157
+ payreq: dict[int, PayReq] = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xync-bot
3
- Version: 0.3.24.dev11
3
+ Version: 0.3.25.dev1
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
@@ -0,0 +1,22 @@
1
+ xync_bot/__main__.py,sha256=OvUQ9A4YZajOgs8CoRhjFzXQnZvNnH8l8MV_brZCOpo,991
2
+ xync_bot/loader.py,sha256=4ZeR-yVMoOmswdLS0UEBG19K7JVcuvH6WpP-_0yAK3I,573
3
+ xync_bot/shared.py,sha256=PPzvt1ewowCInKE1bk2CWHPjnrV2eQJzKxgyxe3h7vk,496
4
+ xync_bot/store.py,sha256=NhUMl0Zr4PKiukQKH8HDA6KRjghjtIcDioNLZArmdhg,6385
5
+ xync_bot/typs.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ xync_bot/routers/__init__.py,sha256=30W7PmD3stkRebGh5J-9CJIlY5P-Ixrtlxb_W9usSo4,1083
7
+ xync_bot/routers/order.py,sha256=ZKWDLyiWrXzcR-aHKLBTBCACwp-P0Vvnr22T-EuLHaM,274
8
+ xync_bot/routers/photo.py,sha256=aq6ImIOoZQYTW-lEy26qjgj5TYAuk4bQjgiCv64mPJs,1203
9
+ xync_bot/routers/vpn.py,sha256=qKK55UrjEZeDvu7ljWXNUFBFgXTPTIEaCT2OAmKWky4,2219
10
+ xync_bot/routers/xicon.png,sha256=O57_kvzhVcCXSoGYZ61m0dW9pizY6gxR8Yj5aeCP0RQ,429283
11
+ xync_bot/routers/cond/__init__.py,sha256=AUP_V1TGUIa8GFTC_V2LF5YYEBrXdfrSm_O8ew5sXU8,4214
12
+ xync_bot/routers/cond/func.py,sha256=m0NWDKunbqDJQmhv_5UnpjxjRzn78GFG94ThOFLVlQo,4720
13
+ xync_bot/routers/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ xync_bot/routers/main/handler.py,sha256=IDvHPcj1Z-MpL6DSlgibxxCZmaaVN3OhByIZZyrK2FM,8758
15
+ xync_bot/routers/pay/cd.py,sha256=yu5U0j8f1RncfagiuycCeUFWwZtLmbT-Mx8G8whfdg8,927
16
+ xync_bot/routers/pay/dep.py,sha256=WvIkLsMHsy6Z8qeQYB0uvHi9xgJ-pCjRad0dSp_GQS0,3956
17
+ xync_bot/routers/pay/handler.py,sha256=7kb3ubkvolJPM9Sd51pnw9tlR9QxblNrIJN4QScgZQ8,10341
18
+ xync_bot/routers/pay/window.py,sha256=HQWDDvknMMbTEt5Zu7cXsr6SaDnFlV43xZU4CF4S2Ps,10987
19
+ xync_bot-0.3.25.dev1.dist-info/METADATA,sha256=QgjczDpDF437LM-dzo3MlstW0Fx6e0264dSQNYWF-yE,751
20
+ xync_bot-0.3.25.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ xync_bot-0.3.25.dev1.dist-info/top_level.txt,sha256=O2IjMc1ryAf0rwIXWohSNT5Kzcs9johgKRDz8lCC0rs,9
22
+ xync_bot-0.3.25.dev1.dist-info/RECORD,,
xync_bot/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import logging
2
- from asyncio import run
3
-
4
- from PGram import Bot
5
- from aiogram.client.default import DefaultBotProperties
6
- from x_model import init_db
7
-
8
- from xync_bot.routers.cond import cr as cr
9
- from xync_bot.routers.pay.dep import Store
10
-
11
- # from xync_bot.routers.main import mr
12
- from xync_bot.routers.pay.handler import pay as pay
13
-
14
- if __name__ == "__main__":
15
- from xync_bot.loader import TOKEN, TORM
16
-
17
- logging.basicConfig(level=logging.INFO)
18
-
19
- async def main() -> None:
20
- cn = await init_db(TORM)
21
- store = Store()
22
- store.glob = await Store.Global()
23
- bot = Bot(TOKEN, [pay], cn, default=DefaultBotProperties(parse_mode="HTML"), store=store)
24
- await bot.start()
25
-
26
- run(main())
@@ -1,20 +0,0 @@
1
- xync_bot/__init__.py,sha256=NSwTrA6NoOWDQK1L7M4qFcbLcCj1od8pgFM-arNv0AQ,713
2
- xync_bot/loader.py,sha256=4ZeR-yVMoOmswdLS0UEBG19K7JVcuvH6WpP-_0yAK3I,573
3
- xync_bot/shared.py,sha256=MlKkTrsT29l7fF6-qAN9FO14cSuXuOuYxbNY5F4S2w4,137
4
- xync_bot/typs.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- xync_bot/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- xync_bot/routers/main.py,sha256=FumWa48ORhV77Df6NbEosHfgmFIe_Y2ci6IkJCvU4Zs,9535
7
- xync_bot/routers/order.py,sha256=ZKWDLyiWrXzcR-aHKLBTBCACwp-P0Vvnr22T-EuLHaM,274
8
- xync_bot/routers/photo.py,sha256=aq6ImIOoZQYTW-lEy26qjgj5TYAuk4bQjgiCv64mPJs,1203
9
- xync_bot/routers/vpn.py,sha256=qKK55UrjEZeDvu7ljWXNUFBFgXTPTIEaCT2OAmKWky4,2219
10
- xync_bot/routers/xicon.png,sha256=O57_kvzhVcCXSoGYZ61m0dW9pizY6gxR8Yj5aeCP0RQ,429283
11
- xync_bot/routers/cond/__init__.py,sha256=It4djVO8AxXL1I76buRz8yYF12dsjXaa4WNtPdb7CFc,4333
12
- xync_bot/routers/cond/func.py,sha256=m0NWDKunbqDJQmhv_5UnpjxjRzn78GFG94ThOFLVlQo,4720
13
- xync_bot/routers/pay/cd.py,sha256=yu5U0j8f1RncfagiuycCeUFWwZtLmbT-Mx8G8whfdg8,927
14
- xync_bot/routers/pay/dep.py,sha256=BOwZV-_p8mtnL-zstsISF9-dAiQG3r0k5gCkL0BvqCc,8519
15
- xync_bot/routers/pay/handler.py,sha256=S6U0uAae9KKRZiAS9C0EBTilVS8Pbegnpw_g7laLBIo,10224
16
- xync_bot/routers/pay/window.py,sha256=5O9oWvt92MmYYM0gtBgxqjTJ3N8WnuMk_rHdY_q1voI,11270
17
- xync_bot-0.3.24.dev11.dist-info/METADATA,sha256=d3pnJZzj16kk_UKHve262UqZOuAJJ5ErqcGqPi5IuOA,752
18
- xync_bot-0.3.24.dev11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- xync_bot-0.3.24.dev11.dist-info/top_level.txt,sha256=O2IjMc1ryAf0rwIXWohSNT5Kzcs9johgKRDz8lCC0rs,9
20
- xync_bot-0.3.24.dev11.dist-info/RECORD,,