xync-bot 0.3.24.dev12__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
- )
@@ -1,4 +1,3 @@
1
- import logging
2
1
  from asyncio import gather
3
2
  from enum import IntEnum
4
3
 
@@ -6,8 +5,6 @@ from aiogram.exceptions import TelegramBadRequest
6
5
  from aiogram.fsm.state import StatesGroup, State
7
6
  from aiogram.types import Message, InlineKeyboardMarkup
8
7
  from pyrogram.types import CallbackQuery
9
- from tortoise.functions import Min
10
- from x_auth.enums import Role
11
8
  from x_model.func import ArrayAgg
12
9
  from xync_schema import models
13
10
 
@@ -60,107 +57,6 @@ class PayStep(IntEnum):
60
57
  pending_receive = 19 # Ожидание поступления (если мы получаем фиат)
61
58
 
62
59
 
63
- flags = {
64
- "RUB": "🇷🇺",
65
- "THB": "🇹🇭",
66
- "IDR": "🇮🇩",
67
- "TRY": "🇹🇷",
68
- "GEL": "🇬🇪",
69
- "VND": "🇻🇳",
70
- "AED": "🇦🇪",
71
- "AMD": "🇦🇲",
72
- "AZN": "🇦🇿",
73
- "CNY": "🇨🇳",
74
- "EUR": "🇪🇺",
75
- "HKD": "🇭🇰",
76
- "INR": "🇮🇳",
77
- "PHP": "🇵🇭",
78
- "USD": "🇺🇸",
79
- }
80
-
81
-
82
- class SingleStore(type):
83
- _store = None
84
-
85
- async def __call__(cls):
86
- if not cls._store:
87
- cls._store = super(SingleStore, cls).__call__()
88
- cls._store.coins = {k: v for k, v in await models.Coin.all().order_by("ticker").values_list("id", "ticker")}
89
- curs = {c.id: c for c in await models.Cur.filter(ticker__in=flags.keys()).order_by("ticker")}
90
- cls._store.curs = curs
91
- cls._store.exs = {k: v for k, v in await models.Ex.all().values_list("id", "name")}
92
- cls._store.pmcurs = {
93
- k: v
94
- for k, v in await models.Pmex.filter(pm__pmcurs__cur_id__in=cls._store.curs.keys())
95
- .annotate(sname=Min("name"))
96
- .group_by("pm__pmcurs__id")
97
- .values_list("pm__pmcurs__id", "sname")
98
- }
99
- cls._store.coinexs = {
100
- c.id: [ex.ex_id for ex in c.coinexs] for c in await models.Coin.all().prefetch_related("coinexs")
101
- }
102
- cls._store.curpms = {
103
- cur_id: ids
104
- for cur_id, ids in await models.Pmcur.filter(cur_id__in=curs.keys())
105
- .annotate(ids=ArrayAgg("id"))
106
- .group_by("cur_id")
107
- .values_list("cur_id", "ids")
108
- }
109
- cls._store.curpms = {
110
- cur_id: ids
111
- for cur_id, ids in await models.Pmcur.filter(cur_id__in=curs.keys())
112
- .annotate(ids=ArrayAgg("id"))
113
- .group_by("cur_id")
114
- .values_list("cur_id", "ids")
115
- }
116
-
117
- return cls._store
118
-
119
-
120
- class Store:
121
- class Global(metaclass=SingleStore):
122
- coins: dict[int, str] # id:ticker
123
- curs: dict[int, models.Cur] # id:Cur
124
- exs: dict[int, str] # id:name
125
- coinexs: dict[int, list[int]] # id:[ex_ids]
126
- pmcurs: dict[int, str] # pmcur_id:name
127
- curpms: dict[int, list[int]] # id:[pmcur_ids]
128
-
129
- class Permanent:
130
- msg_id: int = None
131
- user: models.User = None
132
- actors: dict[int, int] = None # key=ex_id
133
- creds: dict[int, models.Cred] = None # key=cred_id
134
- cur_creds: dict[int, list[int]] = None # pmcur_id:[cred_ids]
135
-
136
- class Current:
137
- is_target: bool = True
138
- is_fiat: bool = None
139
- msg_to_del: Message = None
140
-
141
- class Payment:
142
- t_cur_id: int = None
143
- s_cur_id: int = None
144
- t_coin_id: int = None
145
- s_coin_id: int = None
146
- t_pmcur_id: int = None
147
- s_pmcur_id: int = None
148
- t_ex_id: int = None
149
- s_ex_id: int = None
150
- amount: int | float = None
151
- ppo: int = 1
152
- addr_id: int = None
153
- cred_dtl: str = None
154
- cred_id: int = None
155
- urg: int = 5
156
- pr_id: int = None
157
-
158
- glob: Global
159
- perm: Permanent = Permanent()
160
- pay: Payment = Payment()
161
- curr: Current = Current()
162
-
163
-
164
60
  async def fill_creds(person_id: int) -> tuple[dict[int, models.Cred], dict[int, list[int]]]:
165
61
  cq = models.Cred.filter(person_id=person_id)
166
62
  creds = {c.id: c for c in await cq}
@@ -207,73 +103,6 @@ async def edt(msg: Message, txt: str, rm: InlineKeyboardMarkup):
207
103
  print(msg.bot.store.perm.msg_id, e)
208
104
 
209
105
 
210
- async def xync_have_coin_amount(store: Store) -> bool:
211
- assets = await models.Asset.filter(
212
- addr__coin_id=store.pay.t_coin_id, addr__ex_id=store.pay.t_ex_id, addr__actor__user__role__in=Role.ADMIN
213
- )
214
- return store.pay.amount <= sum(a.free for a in assets)
215
-
216
-
217
- async def client_have_coin_amount(store: Store) -> bool:
218
- assets = await models.Asset.filter(addr__coin_id=store.pay.t_coin_id, addr__actor_id__in=store.perm.actors.values())
219
- return store.pay.amount <= sum(a.free for a in assets)
220
-
221
-
222
- async def need_ppo(store: Store):
223
- cur_id = getattr(store.pay, ("t" if store.curr.is_target else "s") + "_cur_id")
224
- usd_amount = store.pay.amount * store.glob.curs[cur_id].rate
225
- if usd_amount < 50:
226
- return 0
227
- elif usd_amount > 100:
228
- return 2
229
- else:
230
- return 1
231
-
232
-
233
- async def client_target_repr(store: Store) -> tuple[models.Addr | models.Cred, str]:
234
- if store.pay.t_ex_id:
235
- addr_to = (
236
- await models.Addr.filter(
237
- actor__ex_id=store.pay.t_ex_id, coin_id=store.pay.t_coin_id, actor__user=store.perm.user
238
- )
239
- .prefetch_related("actor")
240
- .first()
241
- )
242
- ex, coin = store.glob.exs[store.pay.s_ex_id], store.glob.coins[store.pay.s_coin_id]
243
- if not addr_to:
244
- logging.error(f"No {coin} addr in {ex} for user: {store.perm.user.username_id}")
245
- return addr_to, f"{coin} на {ex} по id: `{addr_to.actor.exid}`"
246
- # иначе: реквизиты для фиата
247
- cur, pm = store.glob.curs[store.pay.t_cur_id], store.glob.pmcurs[store.pay.t_pmcur_id]
248
- cred = store.perm.creds[store.pay.cred_id]
249
- return cred, f"{cur.ticker} на {pm} по номеру: {cred.repr()}"
250
-
251
-
252
- async def get_merch_target(store: Store) -> tuple[models.Addr | models.Cred, str]:
253
- if store.pay.s_ex_id:
254
- addr_in = (
255
- await models.Addr.filter(
256
- actor__ex_id=store.pay.s_ex_id, coin_id=store.pay.s_coin_id, actor__user__role__gte=Role.ADMIN
257
- )
258
- .prefetch_related("actor")
259
- .first()
260
- )
261
- ex, coin = store.glob.exs[store.pay.s_ex_id], store.glob.coins[store.pay.s_coin_id]
262
- if not addr_in:
263
- logging.error(f"No {coin} addr in {ex}")
264
- return addr_in, f"{coin} на {ex} по id: `{addr_in.actor.exid}`"
265
- # иначе: реквизиты для фиатной оплаты
266
- s_pmcur = await models.Pmcur.get(id=store.pay.s_pmcur_id).prefetch_related("pm__grp")
267
- cred = await models.Cred.filter(
268
- **({"pmcur__pm__grp": s_pmcur.pm.grp} if s_pmcur.pm.grp else {"pmcur_id": store.pay.s_pmcur_id}),
269
- person__user__role__gte=Role.ADMIN,
270
- ).first() # todo: order by fiat.target-fiat.amount
271
- cur, pm = store.glob.curs[store.pay.s_cur_id], store.glob.pmcurs[store.pay.s_pmcur_id]
272
- if not cred:
273
- logging.error(f"No {cur.ticker} cred for {pm}")
274
- return cred, f"{cur.ticker} на {pm} по номеру: {cred.repr()}"
275
-
276
-
277
106
  def fmt_sec(sec: int):
278
107
  days = sec // (24 * 3600)
279
108
  sec %= 24 * 3600
@@ -1,88 +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)
25
24
  store.perm.actors = await fill_actors(store.perm.user.person_id)
26
25
 
27
26
 
28
- @pay.callback_query(cd.MoneyType.filter(F.is_fiat))
29
- 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):
30
29
  """Step 2f: Select cur"""
31
- bot.store.curr.is_fiat = True
32
- 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, "Понял, фиат"))
33
32
 
34
33
 
35
- @pay.callback_query(cd.MoneyType.filter(F.is_fiat.__eq__(0)))
36
- 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):
37
36
  """Step 2c: Select coin"""
38
- bot.store.curr.is_fiat = False
39
- await gather(window.coin_select(query.message), ans(query, "Понял, крипта"))
37
+ store.curr.is_fiat = False
38
+ await gather(window.coin_select(query.message, store), ans(query, "Понял, крипта"))
40
39
 
41
40
 
42
- @pay.callback_query(cd.Coin.filter())
43
- 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):
44
43
  """Step 3c: Select target ex"""
45
- setattr(bot.store.pay, ("t" if bot.store.curr.is_target else "s") + "_coin_id", callback_data.id)
46
- 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, "Эта монета есть на следующих биржах"))
47
46
 
48
47
 
49
- @pay.callback_query(cd.Cur.filter())
50
- 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):
51
50
  """Step 3f: Select target pm"""
52
- setattr(bot.store.pay, ("t" if bot.store.curr.is_target else "s") + "_cur_id", callback_data.id)
53
- 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, "Вот платежные системы доступные для этой валюты"))
54
53
 
55
54
 
56
- @pay.callback_query(cd.Pm.filter(F.is_target))
57
- 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):
58
57
  """Step 4f: Fill target cred.detail"""
59
- query.message.bot.store.pay.t_pmcur_id = callback_data.pmcur_id
58
+ store.pay.t_pmcur_id = callback_data.pmcur_id
60
59
  await gather(
61
- window.fill_cred_dtl(query.message),
60
+ window.fill_cred_dtl(query.message, store),
62
61
  ans(query, "Теперь нужны реквизиты"),
63
62
  state.set_state(dep.CredState.detail),
64
63
  )
65
64
 
66
65
 
67
- @pay.callback_query(cd.Cred.filter())
68
- async def h_got_cred(query: CallbackQuery, callback_data: cd.Cred, state: FSMContext):
69
- 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
70
69
  await gather(
71
- 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)
72
71
  )
73
72
 
74
73
 
75
- @pay.message(dep.CredState.detail)
76
- 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):
77
76
  """Step 4.1f: Fill target cred.name"""
78
- msg.bot.store.pay.cred_dtl = msg.text
79
- 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))
80
79
 
81
80
 
82
- @pay.message(dep.CredState.name)
83
- 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):
84
83
  """Step 5f: Save target cred"""
85
- store: Store = msg.bot.store
86
84
  cred, _ = await models.Cred.update_or_create(
87
85
  {"name": msg.text},
88
86
  detail=store.pay.cred_dtl,
@@ -91,30 +89,28 @@ async def h_got_cred_name(msg: Message, state: FSMContext):
91
89
  )
92
90
  store.pay.cred_id = cred.id
93
91
  store.perm.creds[cred.id] = cred
94
- 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))
95
93
 
96
94
 
97
- @pay.callback_query(cd.Ex.filter())
98
- 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):
99
97
  """Step 4c: Save target"""
100
- store: Store = query.message.bot.store
101
98
  ist = store.curr.is_target
102
99
  setattr(store.pay, ("t" if ist else "s") + "_ex_id", callback_data.id)
103
100
  if ist:
104
- await window.amount(query.message)
101
+ await window.amount(query.message, store)
105
102
  actor_id = store.perm.actors[store.pay.t_ex_id]
106
103
  addr = await models.Addr.get(coin_id=store.pay.t_coin_id, actor_id=actor_id)
107
104
  store.pay.addr_id = addr.id
108
105
  else:
109
- await window.set_ppo(query.message)
106
+ await window.set_ppo(query.message, store)
110
107
  await ans(query, f"Биржа {store.glob.exs[callback_data.id]} выбрана")
111
108
  await state.set_state(dep.PaymentState.amount)
112
109
 
113
110
 
114
- @pay.message(dep.PaymentState.amount)
115
- async def h_got_amount(msg: Message, state: FSMContext):
116
- """Step 6: Save target amount"""
117
- 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"""
118
114
  if not msg.text.isnumeric():
119
115
  store.curr.msg_to_del = await msg.answer("Пожалуйста, введите корректное число")
120
116
  return
@@ -123,34 +119,33 @@ async def h_got_amount(msg: Message, state: FSMContext):
123
119
  store.pay.amount = float(msg.text)
124
120
  """Step 7: Select source type"""
125
121
  store.curr.is_target = False
126
- 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())
127
123
 
128
124
 
129
- @pay.callback_query(cd.Pm.filter(F.is_target.__eq__(0)))
130
- async def h_got_source_pm(query: CallbackQuery, callback_data: cd.Pm):
131
- 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):
132
127
  store.pay.s_pmcur_id = callback_data.pmcur_id
133
128
  await gather(
134
- window.set_ppo(query.message),
129
+ window.set_ppo(query.message, store),
135
130
  ans(query, store.glob.pmcurs[callback_data.pmcur_id]),
136
131
  )
137
132
 
138
133
 
139
- @pay.callback_query(cd.Ppo.filter())
140
- async def h_got_ppo(query: CallbackQuery, callback_data: cd.Ppo):
141
- query.message.bot.store.pay.ppo = callback_data.num
142
- 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)))
143
138
 
144
139
 
145
- @pay.callback_query(cd.Time.filter())
146
- async def h_got_urgency(query: CallbackQuery, callback_data: cd.Time):
147
- query.message.bot.store.pay.urg = callback_data.minutes
148
- 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)
149
144
  await ans(query, f"Ok {callback_data.minutes} min.")
150
145
 
151
146
 
152
147
  # ACTIONS
153
- @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)))
154
149
  async def payment_confirmed(query: CallbackQuery, state: FSMContext):
155
150
  await ans(query, None)
156
151
  payed_at = datetime.now()
@@ -164,7 +159,7 @@ async def payment_confirmed(query: CallbackQuery, state: FSMContext):
164
159
  await window.success(query.message)
165
160
 
166
161
 
167
- @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)))
168
163
  async def no_payment(query: CallbackQuery, state: FSMContext):
169
164
  await ans(query, None)
170
165
  await state.update_data(timer_active=False)
@@ -174,80 +169,80 @@ async def no_payment(query: CallbackQuery, state: FSMContext):
174
169
  await state.set_state(dep.Report.text)
175
170
 
176
171
 
177
- @pay.message(dep.Report.text)
172
+ @pr.message(dep.Report.text)
178
173
  async def payment_not_specified(msg: Message, state: FSMContext):
179
174
  await state.update_data(text=msg.text)
180
175
  data = await state.get_data()
181
176
  complaint_text = (
182
177
  f"Жалоба на неполученный платеж:\n"
183
178
  f"Пользователь: @{msg.from_user.username or msg.from_user.id}\n"
184
- f"Детали платежа: {data["text"]}\n"
179
+ f"Детали платежа: {data['text']}\n"
185
180
  f"Время: {msg.date.strftime('%Y-%m-%d %H:%M:%S')}"
186
181
  )
187
182
  await msg.bot.send_message(chat_id="xyncpay", text=complaint_text)
188
183
 
189
184
 
190
185
  # NAVIGATION
191
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_type, cd.PayStep.s_type])))
192
- async def handle_home(query: CallbackQuery, state: FSMContext):
193
- 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, "Создаем платеж заново"))
194
189
 
195
190
 
196
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_coin, cd.PayStep.s_coin])))
197
- 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):
198
193
  await ans(query, None)
199
194
  is_target = await state.get_value("is_target")
200
195
  pref = "t" if is_target else "s"
201
196
  await state.update_data({pref + "_ex_id": None, pref + "_coin_id": None})
202
- await window.coin_select(query.message)
197
+ await window.coin_select(query.message, store)
203
198
 
204
199
 
205
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_cur, cd.PayStep.s_cur])))
206
- 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):
207
202
  await ans(query, None)
208
203
  is_target = await state.get_value("is_target")
209
204
  pref = "t" if is_target else "s"
210
205
  await state.update_data({pref + "_pmcur_id": None, pref + "_cur_id": None})
211
- await window.cur_select(query.message)
206
+ await window.cur_select(query.message, store)
212
207
 
213
208
 
214
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_pm, cd.PayStep.s_pm])))
215
- 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):
216
211
  await ans(query, None)
217
- await window.pm(query.message)
212
+ await window.pm(query.message, store)
218
213
 
219
214
 
220
- @pay.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_cred_dtl)))
221
- 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):
222
217
  await ans(query, None)
223
218
  await state.update_data(detail=None)
224
- await window.fill_cred_dtl(query.message)
219
+ await window.fill_cred_dtl(query.message, store)
225
220
 
226
221
 
227
- @pay.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_cred_name)))
228
- 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):
229
224
  await ans(query, None)
230
225
  await state.update_data(name=None)
231
- await window.fill_cred_name(query.message)
226
+ await window.fill_cred_name(query.message, store)
232
227
 
233
228
 
234
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_ex, cd.PayStep.s_ex])))
235
- 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):
236
231
  await ans(query, None)
237
232
  await state.update_data({("t" if await state.get_value("is_target") else "s") + "ex_id": None})
238
- await window.ex_select(query.message)
233
+ await window.ex_select(query.message, store)
239
234
 
240
235
 
241
- @pay.callback_query(cd.PayNav.filter(F.to.__eq__(cd.PayStep.t_amount)))
242
- 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):
243
238
  await ans(query, None)
244
239
  await state.update_data(amount=None)
245
- await window.amount(query.message)
240
+ await window.amount(query.message, store)
246
241
  await state.set_state(dep.PaymentState.amount)
247
242
 
248
243
 
249
- @pay.callback_query(cd.PayNav.filter(F.to.in_([cd.PayStep.t_pm])))
250
- 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):
251
246
  await ans(query, None)
252
247
  await state.update_data(payment=None)
253
- await window.pm(query.message)
248
+ await window.pm(query.message, store)
@@ -6,22 +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 (
10
- edt,
11
- need_ppo,
12
- Store,
13
- get_merch_target,
14
- fmt_sec,
15
- client_have_coin_amount,
16
- client_target_repr,
17
- )
18
-
9
+ from xync_bot.routers.pay.dep import edt, fmt_sec
19
10
  from xync_bot.routers.pay import cd, dep
11
+ from xync_bot.store import Store
20
12
 
21
13
 
22
- async def type_select(msg: Message):
14
+ async def type_select(msg: Message, store: Store):
23
15
  """Step 1: Select type"""
24
- store: Store = msg.bot.store
25
16
  ist: bool = store.curr.is_target
26
17
  rm = InlineKeyboardMarkup(
27
18
  inline_keyboard=[
@@ -52,11 +43,11 @@ async def type_select(msg: Message):
52
43
  store.perm.msg_id = msg.message_id
53
44
 
54
45
 
55
- async def cur_select(msg: Message):
46
+ async def cur_select(msg: Message, store: Store):
56
47
  """Common using cur func"""
57
48
  builder = InlineKeyboardBuilder()
58
- ist: bool = msg.bot.store.curr.is_target
59
- 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():
60
51
  builder.button(text=cur.ticker + dep.flags[cur.ticker], callback_data=cd.Cur(id=cur_id, is_target=ist))
61
52
  builder.button(text="Назад к выбору типа", callback_data=cd.PayNav(to=cd.PayStep.t_type))
62
53
  builder.adjust(3, 3, 3, 3, 3, 1)
@@ -64,10 +55,9 @@ async def cur_select(msg: Message):
64
55
  await edt(msg, "Выбери валюту котор" + sfx, builder.as_markup())
65
56
 
66
57
 
67
- async def coin_select(msg: Message):
58
+ async def coin_select(msg: Message, store: Store):
68
59
  """Common using coin func"""
69
60
  builder = InlineKeyboardBuilder()
70
- store: Store = msg.bot.store
71
61
  for coin_id, ticker in store.glob.coins.items():
72
62
  builder.button(text=ticker, callback_data=cd.Coin(id=coin_id, is_target=store.curr.is_target))
73
63
  builder.button(
@@ -79,8 +69,7 @@ async def coin_select(msg: Message):
79
69
  await msg.edit_text("Выберите монету котор" + sfx, reply_markup=builder.as_markup())
80
70
 
81
71
 
82
- async def ex_select(msg: Message):
83
- store: Store = msg.bot.store
72
+ async def ex_select(msg: Message, store: Store):
84
73
  ist = store.curr.is_target
85
74
  coin_id = getattr(store.pay, ("t" if ist else "s") + "_coin_id")
86
75
  builder = InlineKeyboardBuilder()
@@ -95,8 +84,7 @@ async def ex_select(msg: Message):
95
84
  await msg.edit_text("На какую биржу?" if ist else "С какой биржи?", reply_markup=keyboard)
96
85
 
97
86
 
98
- async def pm(msg: Message):
99
- store: Store = msg.bot.store
87
+ async def pm(msg: Message, store: Store):
100
88
  ist = store.curr.is_target
101
89
  cur_id = getattr(store.pay, ("t" if ist else "s") + "_cur_id")
102
90
  builder = InlineKeyboardBuilder()
@@ -111,9 +99,8 @@ async def pm(msg: Message):
111
99
  await msg.edit_text("На какую платежную систему?" if ist else "C какой платежной системы?", reply_markup=keyboard)
112
100
 
113
101
 
114
- async def fill_cred_dtl(msg: Message):
102
+ async def fill_cred_dtl(msg: Message, store: Store):
115
103
  builder = InlineKeyboardBuilder()
116
- store: Store = msg.bot.store
117
104
  txt = "В"
118
105
  if cred_ids := store.perm.cur_creds.get(store.pay.t_pmcur_id):
119
106
  for cred_id in cred_ids:
@@ -130,22 +117,20 @@ async def fill_cred_dtl(msg: Message):
130
117
  )
131
118
 
132
119
 
133
- async def fill_cred_name(msg: Message):
120
+ async def fill_cred_name(msg: Message, store: Store):
134
121
  builder = InlineKeyboardBuilder()
135
122
  builder.button(text="Назад к вводу реквизитов", callback_data=cd.PayNav(to=cd.PayStep.t_cred_dtl))
136
123
  builder.button(text="Домой", callback_data=cd.PayNav(to=cd.PayStep.t_type))
137
124
  builder.adjust(2)
138
- store: Store = msg.bot.store
139
125
  cur = store.glob.curs[store.pay.t_cur_id]
140
126
  payment = store.glob.pmcurs[store.pay.t_pmcur_id]
141
127
  detail = store.pay.cred_dtl
142
128
  await edt(msg, f"{cur.ticker}:{payment}:{detail}: Введите имя получателя", builder.as_markup())
143
129
 
144
130
 
145
- async def amount(msg: Message):
131
+ async def amount(msg: Message, store: Store):
146
132
  """Step 5: Filling target amount"""
147
133
  builder = InlineKeyboardBuilder()
148
- store: Store = msg.bot.store
149
134
  if store.curr.is_fiat:
150
135
  cur_coin = store.glob.curs[store.pay.t_cur_id].ticker
151
136
  builder.button(text="Назад к вводу имени", callback_data=cd.PayNav(to=cd.PayStep.t_cred_name))
@@ -161,10 +146,9 @@ async def amount(msg: Message):
161
146
  await edt(msg, f"Введите нужную сумму {cur_coin} для {t_name}", builder.as_markup())
162
147
 
163
148
 
164
- async def set_ppo(msg: Message):
165
- store: Store = msg.bot.store
149
+ async def set_ppo(msg: Message, store: Store):
166
150
  ist = store.curr.is_target
167
- if nppo := await need_ppo(store):
151
+ if nppo := await store.need_ppo():
168
152
  builder = InlineKeyboardBuilder()
169
153
  builder.button(text="Нет", callback_data=cd.Ppo(num=1, is_target=ist)).button(
170
154
  text="Да", callback_data=cd.Ppo(num=2, is_target=ist)
@@ -180,9 +164,8 @@ async def set_ppo(msg: Message):
180
164
  await set_urgency(msg)
181
165
 
182
166
 
183
- async def set_urgency(msg: Message):
184
- store: Store = msg.bot.store
185
- if not store.curr.is_fiat or await client_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():
186
169
  return await create_payreq(msg) # next
187
170
  builder = InlineKeyboardBuilder()
188
171
  (
@@ -198,8 +181,7 @@ async def set_urgency(msg: Message):
198
181
  return await edt(msg, "Сколько можешь ждать?", builder.as_markup())
199
182
 
200
183
 
201
- async def create_payreq(msg: Message):
202
- store: Store = msg.bot.store
184
+ async def create_payreq(msg: Message, store: Store):
203
185
  pay_req, _ = await models.PayReq.update_or_create(
204
186
  {"pay_until": datetime.now() + timedelta(minutes=store.pay.urg)},
205
187
  amount=store.pay.amount,
@@ -209,8 +191,8 @@ async def create_payreq(msg: Message):
209
191
  user=store.perm.user,
210
192
  )
211
193
  store.pay.pr_id = pay_req.id
212
- inp, txt = await get_merch_target(store)
213
- ccred, ctxt = await client_target_repr(store)
194
+ inp, txt = await store.get_merch_target()
195
+ ccred, ctxt = await store.client_target_repr()
214
196
  txt += f"\nИ получите {store.pay.amount} {ctxt} в течение {fmt_sec(store.pay.urg * 60)}"
215
197
  if store.pay.ppo > 1:
216
198
  txt += f" максимум {store.pay.ppo} платежами"
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.dev12
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=u1RCKl3gultJ509W9gbl-ixCCS6B9lgkrDYa_I8Uu2g,10249
15
- xync_bot/routers/pay/handler.py,sha256=aT_Oc_q-h_dX3pU2KhmX7PdRZUeLIuIpagTbBY45GJs,10207
16
- xync_bot/routers/pay/window.py,sha256=-scDDorW2qy9K1gNuGHZVBTaNIdd-7Hh64ZHCFizNVs,11256
17
- xync_bot-0.3.24.dev12.dist-info/METADATA,sha256=MhjS0p9XZSsZJBIwSCYrrr-h8oWNXx2a_0cctuB9FF8,752
18
- xync_bot-0.3.24.dev12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- xync_bot-0.3.24.dev12.dist-info/top_level.txt,sha256=O2IjMc1ryAf0rwIXWohSNT5Kzcs9johgKRDz8lCC0rs,9
20
- xync_bot-0.3.24.dev12.dist-info/RECORD,,