pkscreener 0.46.20250210.702__cp310-cp310-macosx_13_0_arm64.whl → 0.46.20250212.704__cp310-cp310-macosx_13_0_arm64.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.
Files changed (60) hide show
  1. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/README.txt +9 -7
  2. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/MarketMonitor.py +16 -5
  3. pkscreener-0.46.20250212.704.data/purelib/pkscreener/classes/__init__.py +1 -0
  4. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/globals.py +3 -3
  5. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/pkscreenerbot.py +331 -243
  6. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/requirements.txt +1 -1
  7. {pkscreener-0.46.20250210.702.dist-info → pkscreener-0.46.20250212.704.dist-info}/METADATA +12 -10
  8. pkscreener-0.46.20250212.704.dist-info/RECORD +58 -0
  9. pkscreener-0.46.20250210.702.data/purelib/pkscreener/classes/__init__.py +0 -1
  10. pkscreener-0.46.20250210.702.dist-info/RECORD +0 -58
  11. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/Disclaimer.txt +0 -0
  12. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/LICENSE-Others.txt +0 -0
  13. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/LICENSE.txt +0 -0
  14. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/LogoWM.txt +0 -0
  15. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/__init__.py +0 -0
  16. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/ArtTexts.py +0 -0
  17. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/AssetsManager.py +0 -0
  18. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Backtest.py +0 -0
  19. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Barometer.py +0 -0
  20. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/BaseScreeningStatistics.py +0 -0
  21. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/CandlePatterns.py +0 -0
  22. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Changelog.py +0 -0
  23. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/ConfigManager.py +0 -0
  24. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/ConsoleMenuUtility.py +0 -0
  25. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/ConsoleUtility.py +0 -0
  26. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Fetcher.py +0 -0
  27. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/GlobalStore.py +0 -0
  28. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/ImageUtility.py +0 -0
  29. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/MarketStatus.py +0 -0
  30. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/MenuOptions.py +0 -0
  31. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Messenger.py +0 -0
  32. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/OtaUpdater.py +0 -0
  33. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKAnalytics.py +0 -0
  34. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKDataService.py +0 -0
  35. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKDemoHandler.py +0 -0
  36. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKMarketOpenCloseAnalyser.py +0 -0
  37. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKPremiumHandler.py +0 -0
  38. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKScanRunner.py +0 -0
  39. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKScheduledTaskProgress.py +0 -0
  40. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKScheduler.py +0 -0
  41. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKSpreadsheets.py +0 -0
  42. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKTask.py +0 -0
  43. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PKUserRegistration.py +0 -0
  44. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Pktalib.py +0 -0
  45. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Portfolio.py +0 -0
  46. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/PortfolioXRay.py +0 -0
  47. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/ScreeningStatistics.py +0 -0
  48. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/StockScreener.py +0 -0
  49. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/StockSentiment.py +0 -0
  50. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/UserMenuChoicesHandler.py +0 -0
  51. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/Utility.py +0 -0
  52. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/WorkflowManager.py +0 -0
  53. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/classes/keys.py +0 -0
  54. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/courbd.ttf +0 -0
  55. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/pkscreener.ini +0 -0
  56. {pkscreener-0.46.20250210.702.data → pkscreener-0.46.20250212.704.data}/purelib/pkscreener/pkscreenercli.py +0 -0
  57. {pkscreener-0.46.20250210.702.dist-info → pkscreener-0.46.20250212.704.dist-info}/LICENSE +0 -0
  58. {pkscreener-0.46.20250210.702.dist-info → pkscreener-0.46.20250212.704.dist-info}/WHEEL +0 -0
  59. {pkscreener-0.46.20250210.702.dist-info → pkscreener-0.46.20250212.704.dist-info}/entry_points.txt +0 -0
  60. {pkscreener-0.46.20250210.702.dist-info → pkscreener-0.46.20250212.704.dist-info}/top_level.txt +0 -0
@@ -102,6 +102,12 @@ from telegram.ext import (
102
102
  Filters,
103
103
  CallbackContext
104
104
  )
105
+ from PKDevTools.classes.Singleton import SingletonType, SingletonMixin
106
+
107
+ class PKLocalCache(SingletonMixin, metaclass=SingletonType):
108
+ def __init__(self):
109
+ super(PKLocalCache, self).__init__()
110
+ self.registeredIDs = []
105
111
 
106
112
  # Enable logging
107
113
  logging.basicConfig(
@@ -137,6 +143,8 @@ _updater = None
137
143
 
138
144
  TOP_LEVEL_SCANNER_MENUS = ["X", "B", "MI","DV", "P"] #
139
145
  TOP_LEVEL_SCANNER_SKIP_MENUS = ["M", "S", "F", "G", "C", "T", "D", "I", "E", "U", "L", "Z", "P"] # Last item will be skipped.
146
+ TOP_LEVEL_MARKUP_SKIP_MENUS = TOP_LEVEL_SCANNER_SKIP_MENUS[:len(TOP_LEVEL_SCANNER_SKIP_MENUS)-1]
147
+ TOP_LEVEL_MARKUP_SKIP_MENUS.extend(["X","P","B"])
140
148
  INDEX_SKIP_MENUS_1_To_4 = ["W","E","M","Z","0","5","6","7","8","9","10","11","12","13","14","S","15"]
141
149
  INDEX_SKIP_MENUS_5_TO_9 = ["W","E","M","Z","N","0","1","2","3","4","10","11","12","13","14","S","15"]
142
150
  INDEX_SKIP_MENUS_10_TO_15 = ["W","E","M","Z","N","0","1","2","3","4","5","6","7","8","9","S"]
@@ -158,6 +166,21 @@ PIPED_SCAN_SKIP_INDEX_MENUS =["W","N","E","S","0","Z","M","15"]
158
166
  UNSUPPORTED_COMMAND_MENUS =["22","M","Z","0",str(MAX_MENU_OPTION)]
159
167
  SUPPORTED_COMMAND_MENUS = ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45"]
160
168
 
169
+ def registerUser(user,forceFetch=False):
170
+ otpValue, subsModel,subsValidity,alertUser = 0,0,None,None
171
+ if user is not None and (user.id not in PKLocalCache().registeredIDs or forceFetch):
172
+ dbManager = DBManager()
173
+ otpValue, subsModel,subsValidity,alertUser = dbManager.getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
174
+ if str(otpValue).strip() != '0' and user.id not in PKLocalCache().registeredIDs:
175
+ PKLocalCache().registeredIDs.append(alertUser.userid)
176
+ return otpValue, subsModel,subsValidity,alertUser
177
+
178
+ def loadRegisteredUsers():
179
+ dbManager = DBManager()
180
+ users = dbManager.getUsers(fieldName="userid")
181
+ userIDs = [user.userid for user in users]
182
+ PKLocalCache().registeredIDs.extend(userIDs)
183
+
161
184
  def initializeIntradayTimer():
162
185
  try:
163
186
  if (not PKDateUtilities.isTodayHoliday()[0]):
@@ -249,65 +272,17 @@ def matchUTR(update: Update, context: CallbackContext) -> str:
249
272
  updatedResults = "We could not find any transaction details with the provided UTR.\nUPI transaction reference number is a 12-digit alphanumeric/numeric code that serves as a unique identifier for transactions. It is also known as the Unique Transaction Reference (UTR) number.\nYou can find your UPI reference number in the UPI-enabled app you used to make the transaction.\nFor example, you can find your UPI reference number in the History section of Google Pay. \nIn the Paytm app, you can find it by clicking View Details.\n\nIf you still cannot find it, please drop a message with transaction details/snapshot to @ItsOnlyPK to enable subscription."
250
273
  else:
251
274
  updatedResults = "Did you forget to include the UTR number with /Check ?\nYou should use it like this:\n\n/Check UTR_Here\n\nUPI transaction reference number is a 12-digit alphanumeric/numeric code that serves as a unique identifier for transactions. It is also known as the Unique Transaction Reference (UTR) number.\nYou can find your UPI reference number in the UPI-enabled app you used to make the transaction.\nFor example, you can find your UPI reference number in the History section of Google Pay. \nIn the Paytm app, you can find it by clicking View Details.\n\nIf you still cannot find it, please drop a message with transaction details/snapshot to @ItsOnlyPK to enable subscription."
252
- update.message.reply_text(sanitiseTexts(updatedResults), parse_mode="HTML")
275
+ update.message.reply_text(sanitiseTexts(updatedResults), reply_markup=default_markup(user=user),parse_mode="HTML")
253
276
  shareUpdateWithChannel(update=update, context=context, optionChoices=f"/otp\n{updatedResults}")
254
277
  return START_ROUTES
255
278
 
256
- def otp(update: Update, context: CallbackContext) -> str:
257
- global bot_available
258
- updateCarrier = None
259
- if update is None:
260
- return
261
- else:
262
- if update.callback_query is not None:
263
- updateCarrier = update.callback_query
264
- if update.message is not None:
265
- updateCarrier = update.message
266
- if updateCarrier is None:
267
- return
268
- # Get user that sent /start and log his name
269
- user = updateCarrier.from_user
270
- logger.info("User %s started the conversation.", user.first_name)
271
- if not bot_available:
272
- # Sometimes, either the payment does not go through or
273
- # it takes time to process the last month's payment if
274
- # done in the past 24 hours while the last date was today.
275
- # If that happens, we won't be able to run bots or scanners
276
- # without incurring heavy charges. Let's run in the
277
- # unavailable mode instead until this gets fixed.
278
- updatedResults = APOLOGY_TEXT
279
-
280
- if bot_available:
281
- try:
282
- otpValue = 0
283
- dbManager = DBManager()
284
- otpValue, subsModel,subsValidity = dbManager.getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
285
- except Exception as e: # pragma: no cover
286
- logger.error(e)
287
- pass
288
- userText = f"\nUserID: <b>{user.id}</b>"
289
- try:
290
- subscriptionModelNames = "\n<pre>Following basic and premium subscription models are available. Premium subscription allows for unlimited premium scans:\n"
291
- for name,value in PKUserSusbscriptions().subscriptionKeyValuePairs.items():
292
- if name == PKSubscriptionModel.No_Subscription.name:
293
- subscriptionModelNames = f"{subscriptionModelNames}\n{name} : ₹ {value} (Only Basic Scans are free)\n"
294
- else:
295
- subscriptionModelNames = f"{subscriptionModelNames}\n{name.ljust(15)} : ₹ {value}"
296
- subscriptionModelNames = f"{subscriptionModelNames}</pre>\nPlease pay to subscribe:\n\n1. Using UPI(India) to <b>PKScreener@APL</b> \nor\n2. Proudly <b>sponsor</b>: https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra\n\nPlease drop a message to @ItsOnlyPK on Telegram after paying to enable subscription manually or use \n\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically enable subscription after making payment via UPI\n!"
279
+ def editMessageText(query,editedText,reply_markup):
280
+ editedText = f"{PKDateUtilities.currentDateTime()}:\n{editedText}"
281
+ if query is not None and hasattr(query, "edit_message_text"):
282
+ query.edit_message_text(text=editedText, reply_markup=reply_markup,parse_mode="HTML")
297
283
 
298
- subscriptionModelName = PKUserSusbscriptions().subscriptionValueKeyPairs[subsModel]
299
- if subscriptionModelName != PKSubscriptionModel.No_Subscription.name:
300
- subscriptionModelName = f"{subscriptionModelName} (Expires on: {subsValidity})"
301
- except Exception as e:
302
- logger.error(e)
303
- subscriptionModelName = PKSubscriptionModel.No_Subscription.name
304
- pass
305
- if otpValue == 0:
306
- updatedResults = f"We are having difficulty generating OTP for your {userText}. Please try again later."
307
- else:
308
- updatedResults = f"Please use your {userText} \nwith the following OTP to login to PKScreener:\n<b>{otpValue}</b>\n\nYour current subscription : <b>{subscriptionModelName}</b>. {subscriptionModelNames}"
309
- update.message.reply_text(sanitiseTexts(updatedResults), parse_mode="HTML")
310
- shareUpdateWithChannel(update=update, context=context, optionChoices=f"/otp\n{updatedResults}")
284
+ def otp(update: Update, context: CallbackContext) -> str:
285
+ viewSubscriptionOptions(update,context,sendOTP=True)
311
286
  return START_ROUTES
312
287
 
313
288
  def start(update: Update, context: CallbackContext, updatedResults=None, monitorIndex=0,chosenBotMenuOption="") -> str:
@@ -339,32 +314,7 @@ def start(update: Update, context: CallbackContext, updatedResults=None, monitor
339
314
  # The keyboard is a list of button rows, where each row is in turn
340
315
  # a list (hence `[[...]]`).
341
316
  if bot_available:
342
- mns = m0.renderForMenu(asList=True)
343
- if (PKDateUtilities.isTradingTime() and not PKDateUtilities.isTodayHoliday()[0]) or ("PKDevTools_Default_Log_Level" in os.environ.keys()) or sys.argv[0].endswith(".py"):
344
- mns.append(menu().create(f"MI_{monitorIndex}", "👩‍💻 🚀 Intraday Monitor", 2))
345
- if user.username == OWNER_USER:
346
- mns.append(menu().create(f"DV_0", ("✅ Enable Logging" if not configManager.logsEnabled else "🚫 Disable Logging"), 2))
347
- mns.append(menu().create(f"DV_1", "🔄 Restart Bot", 2))
348
-
349
- inlineMenus = []
350
- keyboard = []
351
- rowIndex = 0
352
- iconDict = {"X":"🕵️‍♂️ 🔍 ","B":"📈 🎯 ","P":"🧨 💥 ","MI":"","DV":""}
353
- for mnu in mns:
354
- if mnu.menuKey[0:2] in TOP_LEVEL_SCANNER_MENUS:
355
- rowIndex +=1
356
- inlineMenus.append(
357
- InlineKeyboardButton(
358
- iconDict.get(str(mnu.menuKey[0:2])) + mnu.menuText.split("(")[0],
359
- callback_data="C" + str(mnu.menuKey),
360
- )
361
- )
362
- if rowIndex % 2 == 0:
363
- keyboard.append(inlineMenus)
364
- inlineMenus = []
365
- if len(inlineMenus) > 0:
366
- keyboard.append(inlineMenus)
367
- reply_markup = InlineKeyboardMarkup(keyboard)
317
+ reply_markup = default_markup(user=user,monitorIndex=monitorIndex)
368
318
  cmds = m0.renderForMenu(
369
319
  selectedMenu=None,
370
320
  skip=TOP_LEVEL_SCANNER_SKIP_MENUS[:len(TOP_LEVEL_SCANNER_SKIP_MENUS)-1],
@@ -375,22 +325,23 @@ def start(update: Update, context: CallbackContext, updatedResults=None, monitor
375
325
  reply_markup = None
376
326
 
377
327
  if updatedResults is None:
378
- cmdText = "\n/otp to generate an OTP to login to PKScreener desktop console\n\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically enable subscription after making payment via UPI"
328
+ cmdText = "\n/otp : To generate an OTP to login to PKScreener desktop console\n/check UPI_UTR_HERE_After_Making_Payment : To share transaction reference number to automatically enable subscription after making payment via UPI\n"
379
329
  for cmd in cmds:
380
- cmdText = f"{cmdText}\n\n{cmd.commandTextKey()} for {cmd.commandTextLabel()}"
381
- tosDisclaimerText = "By using this Software, you agree to\n[+] having read through the Disclaimer (https://pkjmesra.github.io/PKScreener/Disclaimer.txt)\n[+] and accept Terms Of Service (https://pkjmesra.github.io/PKScreener/tos.txt) of PKScreener.\n\n[+] If that is not the case, you MUST immediately terminate using PKScreener and exit now!\n\n"
382
- menuText = f"Welcome {user.first_name}, {(user.username)}!\n\n{tosDisclaimerText}Please choose a menu option by selecting a button from below.\n\nYou can also explore a wide variety of all other scanners by typing in \n{cmdText}\n\n OR just use the buttons below to choose."
330
+ if cmd.menuKey not in TOP_LEVEL_MARKUP_SKIP_MENUS:
331
+ cmdText = f"{cmdText}\n{cmd.commandTextKey()} : {cmd.commandTextLabel()}"
332
+ tosDisclaimerText = "By using this Software, you agree to\n[+] having read through the <a href='https://pkjmesra.github.io/PKScreener/Disclaimer.txt'>Disclaimer</a>\n[+] and accept <a href='https://pkjmesra.github.io/PKScreener/tos.txt'>Terms Of Service</a> of PKScreener.\n[+] If that is not the case, you MUST immediately terminate using PKScreener and exit now!\n"
333
+ menuText = f"Welcome {user.first_name}, {(user.username)}!\n{tosDisclaimerText}Please choose a menu option by selecting a button from below.{cmdText}"
383
334
  try:
384
335
  if updateCarrier is not None and hasattr(updateCarrier, "data") and updateCarrier.data is not None and updateCarrier.data == "CP":
385
- menuText = f"Piped Scanners are available using /P . Click on this /P to begin using piped scanners. To use other scanners, choose a menu option by selecting a button from below.\n\nYou can also explore a wide variety of all other scanners by typing in \n{cmdText}\n\n OR just use the buttons below to choose."
336
+ menuText = f"Piped Scanners are available using /P . Click on this /P to begin using piped scanners. To use other scanners, choose a menu option by selecting a button from below.\n{cmdText}"
386
337
  except Exception as e: # pragma: no cover
387
338
  logger.error(e)
388
339
  pass
389
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
340
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
390
341
  else:
391
342
  if not isUserSubscribed(user):
392
- updatedResults = f"Thank you for choosing Intraday Monitor!\n\nThis scan request is, however ,protected and is only available to premium subscribers. It seems like you are not subscribed to the paid/premium subscription to PKScreener.\nPlease checkout all premium options by sending out a request:\n\n/OTP\n\nFor basic/unpaid users, you can try out the following:\n /X_0 StockCode1,StockCode2,etc.\n/X_N\n/X_1\n"
393
- updatedResults = f"{updatedResults}\n\nClick /start if you want to restart the session."
343
+ updatedResults = f"Thank you for choosing Intraday Monitor!\n\nThis scan request is, however, protected and is only available to premium subscribers. It seems like you are not subscribed to the paid/premium subscription to PKScreener.\nPlease checkout all premium options by sending out a request:\n/OTP\n\nFor basic/unpaid users, you can try out the following:\n/X_0 StockCode1,StockCode2,etc.\n/X_N\n/X_1"
344
+ updatedResults = f"{updatedResults}\nClick /start if you want to restart the session."
394
345
  chosenBotMenuOption = f"{chosenBotMenuOption}\nInt. Monitor. MonitorIndex:{monitorIndex}\n{updatedResults}"
395
346
  menuText = updatedResults
396
347
  # Send message with text and appended InlineKeyboard
@@ -402,6 +353,7 @@ def start(update: Update, context: CallbackContext, updatedResults=None, monitor
402
353
  update.message.reply_text(
403
354
  sanitiseTexts(menuText),
404
355
  reply_markup=reply_markup,
356
+ parse_mode="HTML"
405
357
  )
406
358
  if Channel_Id is not None and len(str(Channel_Id)) > 0:
407
359
  context.bot.send_message(
@@ -409,7 +361,7 @@ def start(update: Update, context: CallbackContext, updatedResults=None, monitor
409
361
  text=f"Name: {user.first_name}, Username:@{user.username} with ID: {str(user.id)} started using the bot!\n{chosenBotMenuOption}",
410
362
  parse_mode="HTML",
411
363
  )
412
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
364
+ registerUser(user)
413
365
  # Tell ConversationHandler that we're in state `FIRST` now
414
366
  return START_ROUTES
415
367
 
@@ -583,11 +535,74 @@ def PScanners(update: Update, context: CallbackContext) -> str:
583
535
  )
584
536
  keyboard = [inlineMenus]
585
537
  reply_markup = InlineKeyboardMarkup(keyboard)
586
- if query.message.text == menuText:
587
- menuText = f"{PKDateUtilities.currentDateTime()}:\n{menuText}"
588
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
589
- query.edit_message_text(text=menuText, reply_markup=reply_markup)
590
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
538
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
539
+ editMessageText(query=query,editedText=menuText,reply_markup=reply_markup)
540
+ registerUser(user)
541
+ return START_ROUTES
542
+
543
+ def viewSubscriptionOptions(update:Update,context:CallbackContext,sendOTP=False):
544
+ global bot_available
545
+ updateCarrier = None
546
+ updatedResults= ""
547
+ if update is None:
548
+ return
549
+ else:
550
+ if update.callback_query is not None:
551
+ updateCarrier = update.callback_query
552
+ if update.message is not None:
553
+ updateCarrier = update.message
554
+ if updateCarrier is None:
555
+ return
556
+ # Get user that sent /start and log his name
557
+ user = updateCarrier.from_user
558
+ logger.info("User %s started the conversation.", user.first_name)
559
+ if not bot_available:
560
+ # Sometimes, either the payment does not go through or
561
+ # it takes time to process the last month's payment if
562
+ # done in the past 24 hours while the last date was today.
563
+ # If that happens, we won't be able to run bots or scanners
564
+ # without incurring heavy charges. Let's run in the
565
+ # unavailable mode instead until this gets fixed.
566
+ updatedResults = APOLOGY_TEXT
567
+
568
+ if bot_available:
569
+ try:
570
+ otpValue = 0
571
+ alertUser = None
572
+ dbManager = DBManager()
573
+ otpValue, subsModel,subsValidity,alertUser = registerUser(user,forceFetch=True)
574
+ except Exception as e: # pragma: no cover
575
+ logger.error(e)
576
+ pass
577
+ userText = f"<b>UserID</b> : <code>{user.id}</code>"
578
+ try:
579
+ subscriptionModelNames = "\n<pre>Following basic and premium subscription models are available. Premium subscription allows for unlimited premium scans:\n"
580
+ for name,value in PKUserSusbscriptions().subscriptionKeyValuePairs.items():
581
+ if name == PKSubscriptionModel.No_Subscription.name:
582
+ subscriptionModelNames = f"{subscriptionModelNames}\n₹ {str(value).ljust(6)}: {name} (Only Basic Scans are free)\n"
583
+ else:
584
+ subscriptionModelNames = f"{subscriptionModelNames}\n₹ {str(value).ljust(6)}: {name}"
585
+ subscriptionModelNames = f"{subscriptionModelNames}</pre>\nPlease pay to subscribe:\n1. Using UPI(India) to <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> or\n2. Proudly <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'><b>sponsor</b></a>\n\nPlease drop a message to @ItsOnlyPK on Telegram after paying to enable subscription manually or use \n\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically enable subscription after making payment via UPI\n"
586
+
587
+ subscriptionModelName = PKUserSusbscriptions().subscriptionValueKeyPairs[subsModel]
588
+ if subscriptionModelName != PKSubscriptionModel.No_Subscription.name:
589
+ subscriptionModelName = f"{subscriptionModelName} (Expires on: {subsValidity})"
590
+ except Exception as e:
591
+ logger.error(e)
592
+ subscriptionModelName = PKSubscriptionModel.No_Subscription.name
593
+ pass
594
+ if sendOTP:
595
+ if otpValue == 0:
596
+ updatedResults = f"We are having difficulty generating OTP for your {userText}. Please try again later or reach out to @ItsOnlyPK."
597
+ else:
598
+ updatedResults = f"Please use the following to login to PKScreener:\n{userText}\n<b>OTP</b> : <code>{otpValue}</code>\n\nCurrent subscription : <b>{subscriptionModelName}</b>.\nCurrent alerts balance: <b>₹ {alertUser.balance if alertUser is not None else 0}</b>. {subscriptionModelNames}"
599
+ else:
600
+ updatedResults = f"Current subscription: <b>{subscriptionModelName}</b>.\nCurrent alerts balance: <b>₹ {alertUser.balance if alertUser is not None else 0}</b>. {subscriptionModelNames}"
601
+ if hasattr(updateCarrier, "reply_text"):
602
+ updateCarrier.reply_text(text=sanitiseTexts(updatedResults), reply_markup=default_markup(user=user),parse_mode="HTML")
603
+ elif hasattr(updateCarrier, "edit_message_text"):
604
+ editMessageText(query=updateCarrier,editedText=sanitiseTexts(updatedResults),reply_markup=default_markup(user=user))
605
+ shareUpdateWithChannel(update=update, context=context, optionChoices=f"/otp\n{updatedResults}")
591
606
  return START_ROUTES
592
607
 
593
608
  def subscribeToScannerAlerts(update: Update, context: CallbackContext) -> str:
@@ -622,11 +637,11 @@ def subscribeToScannerAlerts(update: Update, context: CallbackContext) -> str:
622
637
  return START_ROUTES
623
638
  dbManager = DBManager()
624
639
  alertUser = dbManager.alertsForUser(int(user.id))
625
- inlineMenus = []
626
640
  query.answer()
627
641
  menuText = ""
628
642
  requiredBalance = 40 if str(scanId).upper().startswith("P") else 31
629
- payWall = "Please pay to subscribe:\n\n1. Using UPI(India) to PKScreener@APL \nor\n2. Proudly sponsor: https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra\n\nPlease drop a message to @ItsOnlyPK along with UTR and Scan details on Telegram after paying to enable subscription manually or use \n\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically update your balance after making payment via UPI\n! After that you can try re-subscribing!"
643
+ # upi://pay?pa=PKScreener@APL&pn=PKScreener&cu=INR
644
+ payWall = "Please pay to subscribe:\n1. Using UPI(India) to <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> or\n2. Proudly <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>\n\nPlease drop a message to @ItsOnlyPK along with UTR and Scan details on Telegram after paying to enable subscription manually or use \n\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically update your balance after making payment via UPI.\nAfter that you can try re-subscribing!"
630
645
  if alertUser is not None and alertUser.balance >= 0:
631
646
  # User has some balance
632
647
  if len(alertUser.scannerJobs) > 0:
@@ -636,7 +651,7 @@ def subscribeToScannerAlerts(update: Update, context: CallbackContext) -> str:
636
651
  else:
637
652
  if alertUser.balance < requiredBalance:
638
653
  # Insufficient balance
639
- menuText = f"You need at least {requiredBalance} to subscribe to {scanId} ! Your current balance {alertUser.balance} is insufficient. {payWall}"
654
+ menuText = f"You need at least <b>₹ {requiredBalance}</b> to subscribe to <b>{scanId} alerts for a day</b> ! Your current balance <b>₹ {alertUser.balance}</b> is <b>insufficient</b>. {payWall}"
640
655
  else:
641
656
  # Sufficient balance to subscribe to scanId
642
657
  subscribed = dbManager.updateAlertSubscriptionModel(user.id,requiredBalance,scanId)
@@ -647,19 +662,10 @@ def subscribeToScannerAlerts(update: Update, context: CallbackContext) -> str:
647
662
 
648
663
  elif alertUser is None or alertUser.balance == 0:
649
664
  # Either user is not subscribed or has 0 balance
650
- menuText = f"You need at least {requiredBalance} to subscribe to {scanId} ! {payWall}"
665
+ menuText = f"You need at least <b>₹ {requiredBalance}</b> to subscribe to <b>{scanId} alerts for a day</b> ! Your current balance <b>₹ 0</b> is <b>insufficient</b>. {payWall}"
651
666
 
652
- inlineMenus.append(
653
- InlineKeyboardButton(
654
- "Start", callback_data="start"
655
- )
656
- )
657
- keyboard = [inlineMenus]
658
- reply_markup = InlineKeyboardMarkup(keyboard)
659
- if query.message.text == menuText:
660
- menuText = f"{PKDateUtilities.currentDateTime()}:\n{menuText}"
661
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
662
- query.edit_message_text(text=menuText, reply_markup=reply_markup)
667
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
668
+ editMessageText(query=query,editedText=sanitiseTexts(menuText),reply_markup=default_markup(user=user))
663
669
  return START_ROUTES
664
670
 
665
671
 
@@ -756,11 +762,9 @@ def XScanners(update: Update, context: CallbackContext) -> str:
756
762
  )
757
763
  keyboard = [inlineMenus]
758
764
  reply_markup = InlineKeyboardMarkup(keyboard)
759
- if query.message.text == menuText:
760
- menuText = f"{PKDateUtilities.currentDateTime()}:\n{menuText}"
761
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
762
- query.edit_message_text(text=menuText, reply_markup=reply_markup)
763
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
765
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
766
+ editMessageText(query=query,editedText=sanitiseTexts(menuText),reply_markup=reply_markup)
767
+ registerUser(user)
764
768
  return START_ROUTES
765
769
 
766
770
  def getinlineMenuListRow(keyboardRows=[]):
@@ -817,21 +821,23 @@ def Level2(update: Update, context: CallbackContext) -> str:
817
821
  start(update, context)
818
822
  return START_ROUTES
819
823
 
820
- if len(selection) == 2 and selection[0] in ["X","B"] and selection[1] in ["P1","P2","P3"]:
824
+ reply_markup = default_markup(user=user)
825
+ if (len(selection) == 2 and selection[0] in ["X","B"] and selection[1] in ["P1","P2","P3"]) or \
826
+ (len(selection) == 4 and selection[0] in ["P"] and selection[3] in ["P1","P2","P3"]): # Piped scan index options
821
827
  nextOption = ""
822
- if selection[1] == "P2":
828
+ if selection[1] == "P2" or (selection[0] in ["P"] and selection[3] == "P2"):
823
829
  skipMenus = INDEX_SKIP_MENUS_5_TO_9
824
830
  nextOption = "P3"
825
- elif selection[1] == "P3":
831
+ elif selection[1] == "P3" or (selection[0] in ["P"] and selection[3] == "P3"):
826
832
  skipMenus = INDEX_SKIP_MENUS_10_TO_15
827
833
  nextOption = "P1"
828
- elif selection[1] == "P1":
834
+ elif selection[1] == "P1" or (selection[0] in ["P"] and selection[3] == "P1"):
829
835
  skipMenus = INDEX_SKIP_MENUS_1_To_4
830
836
  nextOption = "P2"
831
837
  # Create the menu text labels
832
838
  menuText = (
833
839
  m1.renderForMenu(
834
- m0.find(selection[0]),
840
+ m0.find(selection[0] if selection[0] not in ["P"] else "X"),
835
841
  skip=skipMenus,
836
842
  renderStyle=MenuRenderStyle.STANDALONE,
837
843
  )
@@ -846,7 +852,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
846
852
 
847
853
  # Create the menu buttons
848
854
  mns = m1.renderForMenu(
849
- m0.find(selection[0]),
855
+ m0.find(selection[0] if selection[0] not in ["P"] else "X"),
850
856
  skip=skipMenus,
851
857
  asList=True,
852
858
  )
@@ -856,14 +862,12 @@ def Level2(update: Update, context: CallbackContext) -> str:
856
862
  for mnu in mns:
857
863
  activeInlineRow = getinlineMenuListRow(keyboardRows)
858
864
  activeInlineRow.append(
859
- InlineKeyboardButton(mnu.menuKey, callback_data=str(f"C{selection[0]}_{mnu.menuKey}")))
865
+ InlineKeyboardButton(mnu.menuKey, callback_data=str(f"C{(selection[0]+'_'+selection[1]+'_'+selection[2]) if selection[0] in ["P"] else selection[0]}_{mnu.menuKey}")))
860
866
 
861
867
  keyboard = keyboardRows
862
868
  reply_markup = InlineKeyboardMarkup(keyboard)
863
- if query.message.text == menuText:
864
- menuText = f"{PKDateUtilities.currentDateTime()}:\n{menuText}"
865
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
866
- query.edit_message_text(text=menuText, reply_markup=reply_markup)
869
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
870
+ editMessageText(query=query,editedText=sanitiseTexts(menuText),reply_markup=reply_markup)
867
871
  return START_ROUTES
868
872
  if len(selection) == 2 or (len(selection) == 3 and selection[2] == "P"):
869
873
  if str(selection[1]).isnumeric():
@@ -984,7 +988,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
984
988
  )
985
989
  mns.append(menu().create("H", "Home", 2))
986
990
  mns.append(menu().create("P", "More Options", 2))
987
- elif str(selection[2]).isnumeric():
991
+ elif str(selection[2]).isnumeric() and selection[0].lower() not in ["p"]:
988
992
  preSelection = f"{selection[0]}_{selection[1]}_{selection[2]}"
989
993
  if selection[2] in SCANNER_MENUS_WITH_SUBMENU_SUPPORT:
990
994
  menuText = m3.renderForMenu(
@@ -999,7 +1003,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
999
1003
  skip=["0","M","Z"],
1000
1004
  )
1001
1005
  menuText = f"{menuText}\n\nH > Home"
1002
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
1006
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
1003
1007
  mns.append(menu().create("H", "Home", 2))
1004
1008
  else:
1005
1009
  if selection[2] == "4": # Last N days
@@ -1028,7 +1032,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
1028
1032
  )
1029
1033
  keyboard = keyboardRows
1030
1034
  reply_markup = InlineKeyboardMarkup(keyboard)
1031
- elif len(selection) >= 4:
1035
+ if (len(selection) >= 4 and selection[0].lower() not in ["p"]) or (len(selection) >= 3 and selection[0].lower() in ["p"]):
1032
1036
  if len(selection) == 4:
1033
1037
  if selection[2] in SCANNER_SUBMENUS_CHILDLEVEL_SUPPORT.keys() and selection[3] in SCANNER_SUBMENUS_CHILDLEVEL_SUPPORT[selection[2]]:
1034
1038
  m0.renderForMenu(
@@ -1077,7 +1081,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
1077
1081
  )
1078
1082
  menuText = f"{menuText}\n\nH > Home"
1079
1083
  mns.append(menu().create("H", "Home", 3))
1080
- menuText = f"{menuText}\n\nClick /start if you want to restart the session."
1084
+ menuText = f"{menuText}\nClick /start if you want to restart the session."
1081
1085
  if mns is not None:
1082
1086
  for mnu in mns:
1083
1087
  activeInlineRow = getinlineMenuListRow(keyboardRows)
@@ -1086,21 +1090,57 @@ def Level2(update: Update, context: CallbackContext) -> str:
1086
1090
  reply_markup = InlineKeyboardMarkup(keyboard)
1087
1091
  if len(mns) == 0:
1088
1092
  menuText = ''
1089
- elif len(selection) > 4:
1090
- menuText = ''
1093
+ elif len(selection) > 4 or (len(selection) >= 3 and selection[0].lower() in ["p"]):
1094
+ if (selection[0] in 'P' and ((len(selection) >= 4 and len(selection[3]) == 0) or (len(selection) == 3 and str(selection[2]).isnumeric()))):
1095
+ preSelection = query.data.upper().replace("C", "")
1096
+ skipMenus = ["N"]
1097
+ skipMenus.extend(INDEX_SKIP_MENUS_1_To_4)
1098
+ # Create the menu text labels
1099
+ menuText = (
1100
+ m1.renderForMenu(
1101
+ m0.find("X"),
1102
+ skip=skipMenus,
1103
+ renderStyle=MenuRenderStyle.STANDALONE,
1104
+ )
1105
+ .replace(" ", "")
1106
+ .replace(" ", "")
1107
+ .replace(" ", "")
1108
+ .replace("\t", "")
1109
+ .replace(colorText.FAIL,"").replace(colorText.END,"").replace(colorText.WHITE,"")
1110
+ )
1111
+ menuText = f"{menuText}\n\nH > Home"
1112
+ menuText = f"{menuText}\n\nP2 > More Options"
1091
1113
 
1092
- if selection[0] in 'P' and len(selection[3]) == 0:
1093
- selection[3] = '12' # All stocks
1114
+ # Create the menu buttons
1115
+ mns = m1.renderForMenu(
1116
+ m0.find("X"),
1117
+ skip=skipMenus,
1118
+ asList=True,
1119
+ )
1120
+ mns.append(menu().create("H", "Home", 2))
1121
+ mns.append(menu().create("P2", "Next", 2))
1122
+ inlineMenus = []
1123
+ query.answer()
1124
+ for mnu in mns:
1125
+ inlineMenus.append(
1126
+ InlineKeyboardButton(
1127
+ mnu.menuKey, callback_data=str(f"C{preSelection}_{mnu.menuKey}")
1128
+ )
1129
+ )
1130
+ keyboard = [inlineMenus]
1131
+ reply_markup = InlineKeyboardMarkup(keyboard)
1132
+ elif len(mns) == 0:
1133
+ menuText = ''
1094
1134
 
1095
1135
  if menuText is None or len(menuText) == 0:
1096
1136
  optionChoices = (
1097
- f"{selection[0]} > {selection[1]} > {selection[2]} > {selection[3]}"
1137
+ f"{selection[0]} > {selection[1]} > {selection[2]} > {selection[3]}".replace(" > >","").strip()
1098
1138
  )
1099
- optionChoices = f"{optionChoices}{f' > {selection[4]}' if len(selection) > 4 else ''}"
1139
+ optionChoices = f"{optionChoices}{f' > {selection[4]}' if len(selection) > 4 else ''}".replace(" > >","").strip()
1100
1140
  expectedTime = f"{'10 to 15' if '> 15' in optionChoices else '1 to 2'}"
1101
- menuText = f"Thank you for choosing {optionChoices.replace(' > > ','')}. You will receive the notification/results in about {expectedTime} minutes. It generally takes 1-2 minutes for NSE (2000+) stocks and 10-15 minutes for NASDAQ (7300+).\n\nPKScreener had been free for a long time, but owing to cost/budgeting issues, only a basic set of features will always remain free for everyone. Consider donating to help cover the basic server costs or subscribe to premium, if not subscribed yet:\n\nUPI (India): PKScreener@APL \n\nor\nhttps://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra"
1141
+ menuText = f"Thank you for choosing {optionChoices.replace(' > > ','')}. You will receive the notification/results in about {expectedTime} minutes. It generally takes 1-2 minutes for NSE (2000+) stocks and 10-15 minutes for NASDAQ (7300+).\n\nPKScreener had been free for a long time, but owing to cost/budgeting issues, only a basic set of features will always remain free for everyone. Consider donating to help cover the basic server costs or subscribe to premium, if not subscribed yet:\n\nUPI (India): <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> \n\nor <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>"
1102
1142
 
1103
- reply_markup = default_markup([])
1143
+ reply_markup = default_markup(user=user)
1104
1144
  options = ":".join(selection)
1105
1145
  shouldSendUpdate = launchScreener(
1106
1146
  options=options,
@@ -1110,7 +1150,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
1110
1150
  update=update,
1111
1151
  )
1112
1152
  if not shouldSendUpdate:
1113
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
1153
+ registerUser(user)
1114
1154
  return START_ROUTES
1115
1155
  try:
1116
1156
  if optionChoices != "" and Channel_Id is not None and len(str(Channel_Id)) > 0:
@@ -1127,20 +1167,54 @@ def Level2(update: Update, context: CallbackContext) -> str:
1127
1167
  sendUpdatedMenu(
1128
1168
  menuText=menuText, update=update, context=context, reply_markup=reply_markup
1129
1169
  )
1130
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
1170
+ scanRequest = optionChoices.replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
1171
+ sendSubscriptionOption(update,context,scanRequest)
1172
+ registerUser(user)
1131
1173
  return START_ROUTES
1132
1174
 
1133
- def default_markup(inlineMenus):
1134
- mns = m0.renderForMenu(asList=True)
1175
+ def default_markup(user=None,monitorIndex=0):
1176
+ mns = m0.renderForMenu(selectedMenu=None,
1177
+ skip=TOP_LEVEL_SCANNER_SKIP_MENUS[:len(TOP_LEVEL_SCANNER_SKIP_MENUS)-1],
1178
+ asList=True,
1179
+ renderStyle=MenuRenderStyle.STANDALONE,
1180
+ )
1181
+ if (PKDateUtilities.isTradingTime() and not PKDateUtilities.isTodayHoliday()[0]) or ("PKDevTools_Default_Log_Level" in os.environ.keys()) or sys.argv[0].endswith(".py"):
1182
+ mns.append(menu().create(f"MI_{monitorIndex}", "👩‍💻 🚀 Intraday Monitor", 2))
1183
+ if user is not None and user.username == OWNER_USER:
1184
+ mns.append(menu().create(f"DV_0", ("✅ Enable Logging" if not configManager.logsEnabled else "🚫 Disable Logging"), 2))
1185
+ mns.append(menu().create(f"DV_1", "🔄 Restart Bot", 2))
1186
+ keyboard = []
1187
+ inlineMenus = []
1188
+ lastRowMenus = []
1189
+ rowIndex = 0
1190
+ iconDict = {"X":"🕵️‍♂️ 🔍 ","B":"📈 🎯 ","P":"🧨 💥 ","MI":"","DV":"","VS":"🔔 📣 ","start":"🟢 🏁 "}
1135
1191
  for mnu in mns:
1136
- if mnu.menuKey in TOP_LEVEL_SCANNER_MENUS:
1192
+ if mnu.menuKey[0:2] in TOP_LEVEL_SCANNER_MENUS:
1193
+ rowIndex +=1
1137
1194
  inlineMenus.append(
1138
- InlineKeyboardButton(
1139
- mnu.menuText.split("(")[0],
1140
- callback_data="C" + str(mnu.menuKey),
1141
- )
1195
+ InlineKeyboardButton(
1196
+ iconDict.get(str(mnu.menuKey[0:2])) + mnu.menuText.split("(")[0],
1197
+ callback_data="C" + str(mnu.menuKey),
1142
1198
  )
1143
- keyboard = [inlineMenus]
1199
+ )
1200
+ if rowIndex % 2 == 0:
1201
+ keyboard.append(inlineMenus)
1202
+ inlineMenus = []
1203
+ lastRowMenus.append(
1204
+ InlineKeyboardButton(
1205
+ iconDict.get("VS") + "Subscriptions",
1206
+ callback_data="VS_",
1207
+ )
1208
+ )
1209
+ lastRowMenus.append(
1210
+ InlineKeyboardButton(
1211
+ iconDict.get("start") + "Start",
1212
+ callback_data="start",
1213
+ )
1214
+ )
1215
+ if len(inlineMenus) > 0:
1216
+ keyboard.append(inlineMenus)
1217
+ keyboard.append(lastRowMenus)
1144
1218
  reply_markup = InlineKeyboardMarkup(keyboard)
1145
1219
  return reply_markup
1146
1220
 
@@ -1148,14 +1222,8 @@ def default_markup(inlineMenus):
1148
1222
  def sendUpdatedMenu(menuText, update: Update, context, reply_markup, replaceWhiteSpaces=True):
1149
1223
  try:
1150
1224
  menuText.replace(" ", "").replace(" ", "").replace("\t", "").replace(colorText.FAIL,"").replace(colorText.END,"").replace(colorText.WHITE,"") if replaceWhiteSpaces else menuText
1151
- menuText = f"{menuText}\n\nClick /start if you want to restart the session." if "/start" not in menuText else menuText
1152
- if update.callback_query.message.text == menuText:
1153
- menuText = f"{PKDateUtilities.currentDateTime()}:\n{menuText}"
1154
- update.callback_query.edit_message_text(
1155
- text=menuText,
1156
- parse_mode="HTML",
1157
- reply_markup=reply_markup,
1158
- )
1225
+ menuText = f"{menuText}\nClick /start if you want to restart the session." if "/start" not in menuText else menuText
1226
+ editMessageText(query=update.callback_query,editedText=sanitiseTexts(menuText),reply_markup=reply_markup)
1159
1227
  except Exception as e:# pragma: no cover
1160
1228
  logger.log(e)
1161
1229
  start(update, context)
@@ -1176,16 +1244,14 @@ def launchScreener(options, user, context, optionChoices, update):
1176
1244
  isBasicScanRequest = True
1177
1245
  break
1178
1246
  if not isBasicScanRequest:
1179
- responseText = f"Thank you for choosing {scanRequest}!\n\nThis scan request is,however,protected and is only available to premium subscribers. It seems like you are not subscribed to the paid/premium subscription to PKScreener.\nPlease checkout all premium options by sending out a request:\n\n/OTP\n\nFor basic/unpaid users, you can try out the following:\n /X_0 StockCode1,StockCode2,etc.\n/X_N\n/X_1\n"
1247
+ responseText = f"Thank you for choosing {scanRequest}!\n\nThis {'Backtest' if str(scanRequest).startswith('B') else 'Scan'} request is, however, protected and is only available to premium subscribers. It seems like you are not subscribed to the paid/premium subscription to PKScreener.\nPlease checkout all premium options by sending out a request:\n\n/OTP\n\nFor basic/unpaid users, you can try out the following:\n/X_0 StockCode1,StockCode2,etc.\n/X_N\n/X_1\n"
1180
1248
  if update is not None and update.message is not None:
1181
- update.message.reply_text(sanitiseTexts(responseText))
1249
+ update.message.reply_text(sanitiseTexts(responseText),reply_markup=default_markup(user=user),parse_mode="HTML")
1182
1250
  else:
1183
- responseText = f"{responseText}\n\nClick /start if you want to restart the session."
1184
- update.callback_query.edit_message_text(
1185
- text=responseText,
1186
- reply_markup=default_markup([]),
1187
- )
1251
+ responseText = f"{responseText}\nClick /start if you want to restart the session."
1252
+ editMessageText(query=update.callback_query,editedText=sanitiseTexts(responseText),reply_markup=default_markup(user=user))
1188
1253
  shareUpdateWithChannel(update=update, context=context, optionChoices=responseText)
1254
+ sendSubscriptionOption(update,context,scanRequest)
1189
1255
  return False
1190
1256
 
1191
1257
  if str(optionChoices.upper()).startswith("B"):
@@ -1199,13 +1265,10 @@ def launchScreener(options, user, context, optionChoices, update):
1199
1265
  responseText = f"{responseText}\n\nStock-wise: https://pkjmesra.github.io/PKScreener/Backtest-Reports/PKScreener_{optionChoices}_backtest_result_StockSorted.html"
1200
1266
  responseText = f"{responseText}\n\nOther Reports: https://pkjmesra.github.io/PKScreener/BacktestReports.html"
1201
1267
  if update is not None and update.message is not None:
1202
- update.message.reply_text(sanitiseTexts(responseText))
1268
+ update.message.reply_text(sanitiseTexts(responseText),reply_markup=default_markup(user=user),parse_mode="HTML")
1203
1269
  else:
1204
- responseText = f"{responseText}\n\nClick /start if you want to restart the session."
1205
- update.callback_query.edit_message_text(
1206
- text=responseText,
1207
- reply_markup=default_markup([]),
1208
- )
1270
+ responseText = f"{responseText}\nClick /start if you want to restart the session."
1271
+ editMessageText(query=update.callback_query,editedText=sanitiseTexts(responseText),reply_markup=default_markup(user=user))
1209
1272
  shareUpdateWithChannel(
1210
1273
  update=update, context=context, optionChoices=optionChoices
1211
1274
  )
@@ -1272,15 +1335,35 @@ def BBacktests(update: Update, context: CallbackContext) -> str:
1272
1335
  ]
1273
1336
  reply_markup = InlineKeyboardMarkup(keyboard)
1274
1337
  responseText = "Backtesting NOT implemented yet in this Bot!\n\n\nYou can use backtesting by downloading the software from https://github.com/pkjmesra/PKScreener/"
1275
- responseText = f"{responseText}\n\nClick /start if you want to restart the session."
1276
- query.edit_message_text(
1277
- text=responseText,
1278
- reply_markup=reply_markup,
1279
- )
1280
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
1338
+ responseText = f"{responseText}\nClick /start if you want to restart the session."
1339
+ editMessageText(query=query,editedText=sanitiseTexts(responseText),reply_markup=default_markup(user=user))
1340
+ registerUser(user)
1281
1341
  return START_ROUTES
1282
1342
 
1283
-
1343
+ def sendSubscriptionOption(update:Update,context:CallbackContext,scanId):
1344
+ updateCarrier = None
1345
+ if update is None:
1346
+ return
1347
+ else:
1348
+ if update.callback_query is not None:
1349
+ updateCarrier = update.callback_query
1350
+ if update.message is not None:
1351
+ updateCarrier = update.message
1352
+ if updateCarrier is None:
1353
+ return
1354
+ # Get user that sent /start and log his name
1355
+ user = updateCarrier.from_user
1356
+ reply_markup = {
1357
+ "inline_keyboard": [
1358
+ [{"text": f"Yes! Subscribe", "callback_data": f"SUB_{scanId}"}]
1359
+ ],
1360
+ }
1361
+ message=f"Would you like to subscribe to this (<b>{scanId}</b>) automated scan alert for a day during market hours (NSE - IST timezone)? You will need to pay <b>₹ {'40' if str(scanId).upper().startswith('P') else '31'} (One time per day)</b> for automated alerts to only <b>{scanId}</b> all day on the day of subscription."
1362
+ if len(str(scanId).strip()) > 0 and not str(scanId).startswith("B"):
1363
+ context.bot.send_message(
1364
+ chat_id=user.id, text=message, reply_markup=reply_markup, parse_mode="HTML"
1365
+ )
1366
+
1284
1367
  def end(update: Update, context: CallbackContext) -> str:
1285
1368
  """Returns `ConversationHandler.END`, which tells the
1286
1369
  ConversationHandler that the conversation is over.
@@ -1288,10 +1371,8 @@ def end(update: Update, context: CallbackContext) -> str:
1288
1371
  query = update.callback_query
1289
1372
  query.answer()
1290
1373
  responseText = "See https://github.com/pkjmesra/PKScreener/ for more details or join https://t.me/PKScreener. \n\n\nSee you next time!"
1291
- responseText = f"{responseText}\n\nClick /start if you want to restart the session."
1292
- query.edit_message_text(
1293
- text=responseText
1294
- )
1374
+ responseText = f"{responseText}\nClick /start if you want to restart the session."
1375
+ editMessageText(query=query,editedText=sanitiseTexts(responseText),reply_markup=default_markup(query.from_user))
1295
1376
  return ConversationHandler.END
1296
1377
 
1297
1378
 
@@ -1384,6 +1465,21 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1384
1465
  if not bot_available:
1385
1466
  start(update, context)
1386
1467
  return START_ROUTES
1468
+ updateCarrier = None
1469
+ if update is None:
1470
+ return
1471
+ else:
1472
+ if hasattr(update, "callback_query") and update.callback_query is not None:
1473
+ updateCarrier = update.callback_query
1474
+ if hasattr(update, "message") and update.message is not None:
1475
+ updateCarrier = update.message
1476
+ if hasattr(update, "effective_message") and update.effective_message is not None:
1477
+ updateCarrier = update.effective_message
1478
+ if updateCarrier is None:
1479
+ return
1480
+ # Get user that sent /start and log his name
1481
+ user = updateCarrier.from_user
1482
+
1387
1483
  msg = update.effective_message
1388
1484
  try:
1389
1485
  m = re.match(r"\s*/([0-9a-zA-Z_-]+)\s*(.*)", msg.text)
@@ -1414,7 +1510,7 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1414
1510
  cmdText = f"{cmdText}\n/F_0 STOCK_CODE1,STOCK_CODE2 To find which all scanners had these stock codes(Reverse look up)"
1415
1511
 
1416
1512
  if cmd.upper() in TOP_LEVEL_SCANNER_MENUS:
1417
- shareUpdateWithChannel(update=update, context=context)
1513
+ shareUpdateWithChannel(update=update, context=context,optionChoices=msg)
1418
1514
  m0.renderForMenu(
1419
1515
  selectedMenu=None,
1420
1516
  skip=TOP_LEVEL_SCANNER_SKIP_MENUS[:len(TOP_LEVEL_SCANNER_SKIP_MENUS)-1],
@@ -1437,8 +1533,8 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1437
1533
  if cmd in ["x"]:
1438
1534
  cmdText = f"{cmdText}\n\nFor option 0 <Screen stocks by the stock name>, please type in the command in the following format\n/X_0 SBIN\n or \n/X_0_0 SBIN\nand hit send where SBIN is the NSE stock code.For multiple stocks, you can type in \n/X_0 SBIN,ICICIBANK,OtherStocks\nYou can put in any number of stocks separated by space or comma(,)."
1439
1535
  """Send a message when the command /help is issued."""
1440
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1441
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1536
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1537
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1442
1538
  return START_ROUTES
1443
1539
 
1444
1540
  if update.message is None:
@@ -1446,7 +1542,7 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1446
1542
  return START_ROUTES
1447
1543
 
1448
1544
  if "x_0" in cmd or "x_0_0" in cmd or "b_0" in cmd or "g_0" in cmd or "f_0" in cmd:
1449
- shareUpdateWithChannel(update=update, context=context)
1545
+ shareUpdateWithChannel(update=update, context=context,optionChoices=msg)
1450
1546
  shouldScan = False
1451
1547
  if len(args) > 0:
1452
1548
  shouldScan = True
@@ -1467,17 +1563,19 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1467
1563
  )
1468
1564
  if result:
1469
1565
  sendRequestSubmitted(cmd.upper(), update=update, context=context)
1566
+ scanRequest = cmd.upper().replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
1567
+ sendSubscriptionOption(update,context,scanRequest)
1470
1568
  return START_ROUTES
1471
1569
  else:
1472
1570
  if cmd in ["x"]:
1473
1571
  cmdText = "For option 0 <Screen stocks by the stock name>, please type in the command in the following format\n/X_0 SBIN or /X_0_0 SBIN and hit send where SBIN is the NSE stock code.For multiple stocks, you can type in /X_0 SBIN,ICICIBANK,OtherStocks . You can put in any number of stocks separated by space or comma(,)."
1474
1572
  """Send a message when the command /help is issued."""
1475
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1476
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1573
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1574
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1477
1575
  return START_ROUTES
1478
1576
 
1479
1577
  if "p_" in cmd:
1480
- shareUpdateWithChannel(update=update, context=context)
1578
+ shareUpdateWithChannel(update=update, context=context,optionChoices=msg)
1481
1579
  selection = cmd.split("_")
1482
1580
  if len(selection) == 2:
1483
1581
  m0.renderForMenu(
@@ -1504,8 +1602,8 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1504
1602
  cmdText = (
1505
1603
  f"{cmdText}\n\n{cmd.commandTextKey()} for {cmd.commandTextLabel()}"
1506
1604
  )
1507
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1508
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1605
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1606
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1509
1607
  return START_ROUTES
1510
1608
  elif len(selection) == 3:
1511
1609
  m0.renderForMenu(
@@ -1519,8 +1617,8 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1519
1617
  cmdText = (
1520
1618
  f"{cmdText}\n\n/{cmd.upper()}{indexCmd.commandTextKey().replace('/X','')} for Piped scan of {indexCmd.commandTextLabel().replace('Scanners >',cmd.upper()+' >')}"
1521
1619
  )
1522
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1523
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1620
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1621
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1524
1622
  return START_ROUTES
1525
1623
  elif len(selection) == 4:
1526
1624
  options = ":".join(selection)
@@ -1533,10 +1631,12 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1533
1631
  )
1534
1632
  if result:
1535
1633
  sendRequestSubmitted(cmd.upper(), update=update, context=context)
1634
+ scanRequest = cmd.upper().replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
1635
+ sendSubscriptionOption(update,context,scanRequest)
1536
1636
  return START_ROUTES
1537
1637
 
1538
1638
  if "x_" in cmd or "b_" in cmd or "g_" in cmd:
1539
- shareUpdateWithChannel(update=update, context=context)
1639
+ shareUpdateWithChannel(update=update, context=context,optionChoices=msg)
1540
1640
  selection = cmd.split("_")
1541
1641
  if len(selection) == 2:
1542
1642
  m0.renderForMenu(
@@ -1568,14 +1668,16 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1568
1668
  )
1569
1669
  if result:
1570
1670
  sendRequestSubmitted(cmd.upper(), update=update, context=context)
1671
+ scanRequest = cmd.upper().replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
1672
+ sendSubscriptionOption(update,context,scanRequest)
1571
1673
  return START_ROUTES
1572
1674
  elif (
1573
1675
  "x_" in cmd and selectedMenu.menuKey == "0"
1574
1676
  ): # a specific stock by name
1575
1677
  cmdText = "For option 0 <Screen stocks by the stock name>, please type in the command in the following format\n/X_0 SBIN or /X_0_0 SBIN and hit send where SBIN is the NSE stock code.For multiple stocks, you can type in /X_0 SBIN,ICICIBANK,OtherStocks. You can put in any number of stocks separated by space or comma(,)."
1576
1678
  """Send a message when the command /help is issued."""
1577
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1578
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1679
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1680
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1579
1681
  return START_ROUTES
1580
1682
  cmds = m2.renderForMenu(
1581
1683
  selectedMenu=selectedMenu,
@@ -1587,8 +1689,8 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1587
1689
  cmdText = (
1588
1690
  f"{cmdText}\n\n{cmd.commandTextKey()} for {cmd.commandTextLabel()}"
1589
1691
  )
1590
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1591
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1692
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1693
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1592
1694
  return START_ROUTES
1593
1695
  elif len(selection) == 3:
1594
1696
  m0.renderForMenu(
@@ -1625,8 +1727,8 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1625
1727
  )
1626
1728
  for cmd in cmds:
1627
1729
  cmdText = f"{cmdText}\n\n{cmd.commandTextKey()} for {cmd.commandTextLabel()}"
1628
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1629
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1730
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1731
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1630
1732
  return START_ROUTES
1631
1733
  else:
1632
1734
  if selection[2] == "4": # Last N days
@@ -1683,8 +1785,8 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1683
1785
  )
1684
1786
  for cmd in cmds:
1685
1787
  cmdText = f"{cmdText}\n\n{cmd.commandTextKey()} for {cmd.commandTextLabel()}"
1686
- cmdText = f"{cmdText}\n\nClick /start if you want to restart the session."
1687
- update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"))
1788
+ cmdText = f"{cmdText}\nClick /start if you want to restart the session."
1789
+ update.message.reply_text(sanitiseTexts(f"Choose an option:\n{cmdText}"),reply_markup=default_markup(user=user),parse_mode="HTML")
1688
1790
  return START_ROUTES
1689
1791
 
1690
1792
  options = ":".join(selection)
@@ -1697,31 +1799,39 @@ def command_handler(update: Update, context: CallbackContext) -> None:
1697
1799
  )
1698
1800
  if result:
1699
1801
  sendRequestSubmitted(cmd.upper(), update=update, context=context)
1802
+ scanRequest = cmd.upper().replace(" ", "").replace(">", "_").replace(":","_").replace("_D","").upper()
1803
+ sendSubscriptionOption(update,context,scanRequest)
1700
1804
  return START_ROUTES
1701
1805
  if cmd == "y" or cmd == "h":
1702
- shareUpdateWithChannel(update=update, context=context)
1806
+ shareUpdateWithChannel(update=update, context=context,optionChoices=msg)
1703
1807
  from pkscreener.globals import showSendConfigInfo, showSendHelpInfo
1704
1808
  if cmd == "y":
1705
1809
  showSendConfigInfo(defaultAnswer='Y',user=str(update.message.from_user.id))
1706
1810
  elif cmd == "h":
1707
1811
  showSendHelpInfo(defaultAnswer='Y',user=str(update.message.from_user.id))
1708
- # result = launchScreener(
1709
- # options=f"{cmd.upper()}:",
1710
- # user=update.message.from_user,
1711
- # context=context,
1712
- # optionChoices=cmd.upper(),
1713
- # update=update,
1714
- # )
1715
- # sendRequestSubmitted(cmd.upper(), update=update, context=context)
1716
1812
  return START_ROUTES
1717
- update.message.reply_text(sanitiseTexts(f"{cmd.upper()} : Not implemented yet!"))
1813
+ update.message.reply_text(sanitiseTexts(f"{cmd.upper()} : Not implemented yet!"),reply_markup=default_markup(user=user),parse_mode="HTML")
1718
1814
  help_command(update=update, context=context)
1719
1815
 
1720
1816
 
1721
1817
  def sendRequestSubmitted(optionChoices, update, context):
1722
- menuText = f"Thank you for choosing {optionChoices}. You will receive the notification/results in about 1-2 minutes! \n\nConsider donating to help keep this project going:\nUPI: PKScreener@APL \nor\nhttps://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra"
1723
- update.message.reply_text(sanitiseTexts(menuText))
1724
- help_command(update=update, context=context)
1818
+ updateCarrier = None
1819
+ if update is None:
1820
+ return
1821
+ else:
1822
+ if hasattr(update, "callback_query") and update.callback_query is not None:
1823
+ updateCarrier = update.callback_query
1824
+ if hasattr(update, "message") and update.message is not None:
1825
+ updateCarrier = update.message
1826
+ if hasattr(update, "effective_message") and update.effective_message is not None:
1827
+ updateCarrier = update.effective_message
1828
+ if updateCarrier is None:
1829
+ return
1830
+ # Get user that sent /start and log his name
1831
+ user = updateCarrier.from_user
1832
+ menuText = f"Thank you for choosing {optionChoices}. You will receive the notification/results in about 1-2 minutes! \n\nConsider donating to help keep this project going:\nUPI: <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> \nor <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>"
1833
+ update.message.reply_text(sanitiseTexts(menuText),reply_markup=default_markup(user=user),parse_mode="HTML")
1834
+ # help_command(update=update, context=context)
1725
1835
  shareUpdateWithChannel(
1726
1836
  update=update, context=context, optionChoices=optionChoices
1727
1837
  )
@@ -1762,15 +1872,16 @@ def help_command(update: Update, context: CallbackContext) -> None:
1762
1872
  asList=True,
1763
1873
  renderStyle=MenuRenderStyle.STANDALONE,
1764
1874
  )
1765
- cmdText = "\n/otp to generate an OTP to login to PKScreener desktop console\n\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically enable subscription after making payment via UPI"
1875
+ cmdText = "/otp to generate an OTP to login to PKScreener desktop console\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically enable subscription after making payment via UPI"
1766
1876
  for cmd in cmds:
1767
- cmdText = f"{cmdText}\n\n{cmd.commandTextKey()} for {cmd.commandTextLabel()}"
1768
- reply_markup = default_markup([])
1877
+ if cmd.menuKey not in TOP_LEVEL_MARKUP_SKIP_MENUS:
1878
+ cmdText = f"{cmdText}\n{cmd.commandTextKey()} : {cmd.commandTextLabel()}"
1769
1879
 
1770
1880
  """Send a message when the command /help is issued."""
1771
1881
  if update is not None and update.message is not None:
1772
1882
  update.message.reply_text(
1773
- sanitiseTexts(f"You can begin by typing in /start (Recommended) and hit send!\n\nOR\n\nChoose an option:\n{cmdText}\n\nWe recommend you start by clicking on this /start")
1883
+ sanitiseTexts(f"You can begin by typing in /start (Recommended) and hit send!\nOR\nChoose an option:\n{cmdText}\n\nWe recommend you start by clicking on this /start"),
1884
+ reply_markup=default_markup(user=user),parse_mode="HTML"
1774
1885
  ) # \n\nThis bot restarts every hour starting at 5:30am IST until 10:30pm IST to keep it running on free servers. If it does not respond, please try again in a minutes to avoid the restart duration!
1775
1886
  query = update.message
1776
1887
  message = f"Name: <b>{query.from_user.first_name}</b>, Username:@{query.from_user.username} with ID: <b>@{str(query.from_user.id)}</b> began using the bot!"
@@ -1778,8 +1889,7 @@ def help_command(update: Update, context: CallbackContext) -> None:
1778
1889
  context.bot.send_message(
1779
1890
  chat_id=int(f"-{Channel_Id}"), text=message, parse_mode="HTML"
1780
1891
  )
1781
- DBManager().getOTP(user.id,user.username,f"{user.first_name} {user.last_name}",validityIntervalInSeconds=configManager.otpInterval)
1782
-
1892
+ registerUser(user)
1783
1893
 
1784
1894
  def _shouldAvoidResponse(update):
1785
1895
  sentFrom = []
@@ -1957,30 +2067,6 @@ def runpkscreenerbot(availability=True) -> None:
1957
2067
  # ^ means "start of line/string"
1958
2068
  # $ means "end of line/string"
1959
2069
  # So ^ABC$ will only allow 'ABC'
1960
- # conv_handler = ConversationHandler(
1961
- # entry_points=[CommandHandler("start", start),
1962
- # CommandHandler("otp", otp),
1963
- # CommandHandler("check", matchUTR)],
1964
- # states={
1965
- # START_ROUTES: [
1966
- # CallbackQueryHandler(XScanners, pattern="^" + str("CX") + "$"),
1967
- # CallbackQueryHandler(XScanners, pattern="^" + str("CB") + "$"),
1968
- # CallbackQueryHandler(PScanners, pattern="^" + str("CP") + "$"),
1969
- # CallbackQueryHandler(XScanners, pattern="^" + str("CMI_")),
1970
- # CallbackQueryHandler(XDevModeHandler, pattern="^" + str("CDV_")),
1971
- # # CallbackQueryHandler(XScanners, pattern="^" + str("CG") + "$"),
1972
- # CallbackQueryHandler(Level2, pattern="^" + str("CX_")),
1973
- # CallbackQueryHandler(Level2, pattern="^" + str("CB_")),
1974
- # CallbackQueryHandler(Level2, pattern="^" + str("CP_")),
1975
- # CallbackQueryHandler(subscribeToScannerAlerts, pattern="^" + str("SUB_")),
1976
- # # CallbackQueryHandler(Level2, pattern="^" + str("CG_")),
1977
- # CallbackQueryHandler(end, pattern="^" + str("CZ") + "$"),
1978
- # CallbackQueryHandler(start, pattern="^"),
1979
- # ],
1980
- # END_ROUTES: [],
1981
- # },
1982
- # fallbacks=[CommandHandler("start", start)],
1983
- # )
1984
2070
  conv_handler = ConversationHandler(
1985
2071
  entry_points=[CommandHandler("start", start),
1986
2072
  CommandHandler("otp", otp),
@@ -1997,6 +2083,7 @@ def runpkscreenerbot(availability=True) -> None:
1997
2083
  CallbackQueryHandler(Level2, pattern="^" + str("CB_")),
1998
2084
  CallbackQueryHandler(Level2, pattern="^" + str("CP_")),
1999
2085
  CallbackQueryHandler(subscribeToScannerAlerts, pattern="^" + str("SUB_")),
2086
+ CallbackQueryHandler(viewSubscriptionOptions, pattern="^" + str("VS_")),
2000
2087
  # CallbackQueryHandler(Level2, pattern="^" + str("CG_")),
2001
2088
  CallbackQueryHandler(end, pattern="^" + str("CZ") + "$"),
2002
2089
  CallbackQueryHandler(start, pattern="^"),
@@ -2025,6 +2112,7 @@ def runpkscreenerbot(availability=True) -> None:
2025
2112
  if bot_available:
2026
2113
  # Run the intraday monitor
2027
2114
  initializeIntradayTimer()
2115
+ loadRegisteredUsers()
2028
2116
  # Run the bot until the user presses Ctrl-C
2029
2117
  # application.run_polling(allowed_updates=Update.ALL_TYPES)
2030
2118
  # Start the Bot