mirror of
https://github.com/cupcakearmy/mercatus.git
synced 2024-11-01 08:14:10 +01:00
new menu for more granular control over the single stocks
This commit is contained in:
parent
6f0930a0cc
commit
e1c93963d9
@ -1,37 +0,0 @@
|
||||
import asyncio
|
||||
import threading
|
||||
|
||||
|
||||
def interval(every: float or int, autorun=False, iterations=-1, isolated=False, *args_root, **kwargs_root):
|
||||
def wrapper(fn):
|
||||
|
||||
async def decorator(*args, **kwargs):
|
||||
it = 0
|
||||
first = True
|
||||
while iterations == -1 or it < iterations:
|
||||
it += 1
|
||||
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
await asyncio.sleep(every)
|
||||
|
||||
await fn(*args, **kwargs)
|
||||
|
||||
def capsule(*args, **kwargs):
|
||||
def loop_in_thread(l):
|
||||
asyncio.set_event_loop(l)
|
||||
l.run_until_complete(decorator(*args, **kwargs))
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
threading.Thread(target=loop_in_thread, args=(loop,)).start()
|
||||
|
||||
if autorun:
|
||||
if isolated:
|
||||
capsule(*args_root, **kwargs_root)
|
||||
else:
|
||||
asyncio.run(decorator(*args_root, **kwargs_root))
|
||||
else:
|
||||
return capsule if isolated else decorator
|
||||
|
||||
return wrapper
|
@ -1,23 +1,35 @@
|
||||
from enum import Enum
|
||||
|
||||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardRemove, ParseMode
|
||||
from telegram.ext import CommandHandler, MessageHandler, Filters, ConversationHandler, CallbackQueryHandler, CallbackContext
|
||||
|
||||
from utils import Section
|
||||
MENU, API_KEY, ENABLED = range(3)
|
||||
|
||||
MENU, API_KEY, FREQUENCY, ENABLED = range(4)
|
||||
|
||||
class Section(Enum):
|
||||
Watchlist = 'watchlist' # The list of Stocks/ETF to watch
|
||||
Code = 'code' # Market code for a given stock, etf, etc.
|
||||
API_Key = 'api_key' # Alpha Vantage API Key
|
||||
Running = 'running' # Currently sending updates. Avoid overloading the API
|
||||
Enabled = 'enabled' # Whether the bot should send automatic updates
|
||||
Interval = 'interval' # Time axis of the graph
|
||||
Frequency = 'frequency' # How ofter updates should be sent
|
||||
LastRun = 'last_run' # Last time an update was sent to the user
|
||||
CurrentToEdit = 'current_to_edit' # Current element to edit in the conversation handler
|
||||
|
||||
|
||||
def show_menu(update: Update, context: CallbackContext):
|
||||
keyboard = [
|
||||
[InlineKeyboardButton('API Key', callback_data=API_KEY)],
|
||||
[InlineKeyboardButton('Auto Updates', callback_data=ENABLED)],
|
||||
[InlineKeyboardButton('Frequency', callback_data=FREQUENCY)],
|
||||
[InlineKeyboardButton(
|
||||
f'Turn {"off" if context.user_data.setdefault(Section.Enabled.value, True) else "on"} global auto updates',
|
||||
callback_data=ENABLED)],
|
||||
[InlineKeyboardButton('Done', callback_data=ConversationHandler.END)],
|
||||
]
|
||||
update.effective_user.send_message(
|
||||
'_Current settings:_\n'
|
||||
f'API Key: *{context.user_data[Section.API_Key.value]}*\n'
|
||||
f'Auto Updates: *{context.user_data[Section.Enabled.value]}*\n'
|
||||
f'Frequency: *{context.user_data[Section.Frequency.value]}*\n'
|
||||
f'API Key: *{context.user_data.get(Section.API_Key.value, "No Api key set")}*\n'
|
||||
f'Global auto updates: *{context.user_data[Section.Enabled.value]}*\n'
|
||||
'\nWhat settings do you want to configure?',
|
||||
parse_mode=ParseMode.MARKDOWN,
|
||||
reply_markup=InlineKeyboardMarkup(keyboard, one_time_keyboard=True)
|
||||
@ -35,27 +47,7 @@ def show_menu_api_key(update: Update, context: CallbackContext):
|
||||
return API_KEY
|
||||
|
||||
|
||||
def show_menu_frequency(update: Update, context: CallbackContext):
|
||||
keyboard = [
|
||||
[InlineKeyboardButton('2 minutes', callback_data='2m'), InlineKeyboardButton(
|
||||
'30 minutes', callback_data='30m')],
|
||||
[InlineKeyboardButton('hour', callback_data='1h'), InlineKeyboardButton(
|
||||
'4 hours', callback_data='4h')],
|
||||
[InlineKeyboardButton('12 hours', callback_data='12h'), InlineKeyboardButton(
|
||||
'day', callback_data='1d')],
|
||||
[InlineKeyboardButton('3 days', callback_data='3d'), InlineKeyboardButton(
|
||||
'week', callback_data='1w')],
|
||||
[InlineKeyboardButton('Cancel', callback_data='cancel')],
|
||||
]
|
||||
update.effective_user.send_message(
|
||||
'Send me updates every: ⬇',
|
||||
reply_markup=InlineKeyboardMarkup(keyboard)
|
||||
)
|
||||
|
||||
return FREQUENCY
|
||||
|
||||
|
||||
def config(update: Update, context: CallbackContext):
|
||||
def init(update: Update, context: CallbackContext):
|
||||
context.bot.delete_message(
|
||||
chat_id=update.message.chat_id,
|
||||
message_id=update.message.message_id,
|
||||
@ -73,8 +65,6 @@ def menu(update: Update, context: CallbackContext):
|
||||
|
||||
if selected == API_KEY:
|
||||
return show_menu_api_key(update, context)
|
||||
elif selected == FREQUENCY:
|
||||
return show_menu_frequency(update, context)
|
||||
elif selected == ENABLED:
|
||||
toggle_enabled(update, context)
|
||||
else:
|
||||
@ -89,25 +79,8 @@ def set_api_key(update, context):
|
||||
return show_menu(update, context)
|
||||
|
||||
|
||||
def set_frequency(update: Update, context: CallbackContext):
|
||||
selected = update.callback_query.data
|
||||
|
||||
if selected != 'cancel':
|
||||
update.callback_query.edit_message_text(f'Saved {selected} 💪')
|
||||
context.user_data[Section.Frequency.value] = selected
|
||||
else:
|
||||
context.bot.delete_message(
|
||||
chat_id=update.callback_query.message.chat_id,
|
||||
message_id=update.callback_query.message.message_id,
|
||||
)
|
||||
|
||||
return show_menu(update, context)
|
||||
|
||||
|
||||
def toggle_enabled(update: Update, context: CallbackContext):
|
||||
new = not context.user_data.setdefault(Section.Enabled.value, True)
|
||||
context.user_data[Section.Enabled.value] = new
|
||||
update.effective_user.send_message('Auto updates enabled' if new else 'Auto updates disabled')
|
||||
context.user_data[Section.Enabled.value] = not context.user_data[Section.Enabled.value]
|
||||
|
||||
return show_menu(update, context)
|
||||
|
||||
@ -118,16 +91,13 @@ def cancel(update: Update, context: CallbackContext):
|
||||
|
||||
|
||||
config_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('config', config)],
|
||||
|
||||
entry_points=[CommandHandler('settings', init)],
|
||||
states={
|
||||
MENU: [CallbackQueryHandler(menu)],
|
||||
API_KEY: [
|
||||
CommandHandler('cancel', cancel),
|
||||
MessageHandler(Filters.all, set_api_key),
|
||||
],
|
||||
FREQUENCY: [CallbackQueryHandler(set_frequency)],
|
||||
},
|
||||
|
||||
fallbacks=[CommandHandler('cancel', cancel)]
|
||||
)
|
||||
|
@ -1,74 +1,97 @@
|
||||
from asyncio import sleep, run
|
||||
from datetime import datetime
|
||||
from threading import Timer
|
||||
|
||||
from pytimeparse import parse
|
||||
from telegram import Update, ParseMode
|
||||
from telegram.ext import CallbackContext
|
||||
from telegram import Update, ParseMode, ReplyKeyboardRemove
|
||||
from telegram.ext import CallbackContext, ConversationHandler
|
||||
from telegram.ext.dispatcher import run_async
|
||||
|
||||
from commands.config import Section
|
||||
from market import Market
|
||||
from text import INTRO_TEXT
|
||||
from utils import Section, persistence, updater, current_timestamp, delta_timestamp
|
||||
from utils import persistence, updater, current_timestamp, delta_timestamp
|
||||
|
||||
SENDING = False
|
||||
|
||||
|
||||
def error(update: Update, context: CallbackContext):
|
||||
print(context.error)
|
||||
def error_handler(update: Update, context: CallbackContext):
|
||||
print('Error: ', context.error)
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext):
|
||||
def start_handler(update: Update, context: CallbackContext):
|
||||
update.message.reply_markdown(INTRO_TEXT)
|
||||
|
||||
|
||||
def help_handler(update: Update, context: CallbackContext):
|
||||
update.message.reply_markdown(INTRO_TEXT)
|
||||
|
||||
|
||||
def stop_handler(update: Update, context: CallbackContext):
|
||||
context.user_data.clear()
|
||||
update.message.reply_text('You and your data were deleted 🗑')
|
||||
|
||||
|
||||
@run_async
|
||||
def data(update: Update, context: CallbackContext):
|
||||
delta = current_timestamp() - context.user_data.setdefault(Section.Interval.value, delta_timestamp(days=365))
|
||||
send_update_to_user(user=update.effective_user['id'], delta=delta)
|
||||
send_update_to_user(update.effective_user['id'], False)
|
||||
|
||||
|
||||
def send_update_to_user(user: str, delta: int):
|
||||
def send_update_to_user(user: str, auto, codes=None):
|
||||
user_data = None
|
||||
try:
|
||||
user_data = persistence.user_data[user]
|
||||
running = user_data.setdefault(Section.Running.value, False)
|
||||
print(f'Running {user} - {user_data}')
|
||||
|
||||
if Section.API_Key.value not in user_data:
|
||||
updater.bot.send_message(user, text='API Key not set ⛔️')
|
||||
updater.bot.send_message(user, text='API Key not set ⛔️\nSet in /settings')
|
||||
return
|
||||
|
||||
if running:
|
||||
updater.bot.send_message(user, text='Already running 🏃')
|
||||
return
|
||||
|
||||
print(f'Sending updates to {user}')
|
||||
user_data[Section.Running.value] = True
|
||||
user_data[Section.LastRun.value] = current_timestamp()
|
||||
|
||||
market = Market(user_data[Section.API_Key.value])
|
||||
updater.bot.send_message(user, text='Getting updates 🌎')
|
||||
now = current_timestamp()
|
||||
if auto:
|
||||
updater.bot.send_message(user, text='Getting updates 🌎')
|
||||
|
||||
first = True
|
||||
for item in user_data.get(Section.Watchlist.value, []):
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
# Wait to not overload the api
|
||||
msg = updater.bot.send_message(user, text='Waiting 60 seconds for API... ⏳')
|
||||
run(sleep(60))
|
||||
for code in codes if codes else user_data.get(Section.Watchlist.value, {}).keys():
|
||||
try:
|
||||
code_data = persistence.user_data[user][Section.Watchlist.value][code]
|
||||
code_data = code_data['value']
|
||||
print(code, code_data)
|
||||
last_run = code_data[Section.LastRun.value]
|
||||
frequency = parse(code_data[Section.Frequency.value])
|
||||
interval = parse(code_data[Section.Interval.value])
|
||||
print(code, last_run + frequency, now, last_run + frequency - now)
|
||||
|
||||
if auto and last_run + frequency > now:
|
||||
continue
|
||||
persistence.user_data[user][Section.Watchlist.value][code][Section.LastRun.value] = current_timestamp()
|
||||
persistence.flush()
|
||||
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
# Wait to not overload the api
|
||||
msg = updater.bot.send_message(user, text='Waiting 60 seconds for API... ⏳')
|
||||
run(sleep(60))
|
||||
msg.delete()
|
||||
|
||||
msg = updater.bot.send_message(user, text=f'Calculating {code}... ⏳')
|
||||
delta = datetime.fromtimestamp(now - interval)
|
||||
chart = market.get_wma(code, delta)
|
||||
msg.delete()
|
||||
updater.bot.send_photo(user, photo=chart, disable_notification=True,
|
||||
caption=f'{code} - {code_data[Section.Interval.value]}')
|
||||
except Exception as e:
|
||||
print(f'❌ {user} - {e}')
|
||||
updater.bot.send_message(user, text=f'There was an error ⚠️\n {e}')
|
||||
|
||||
msg = updater.bot.send_message(user, text=f'Calculating {item}... ⏳')
|
||||
chart = market.get_wma(item, datetime.fromtimestamp(delta))
|
||||
msg.delete()
|
||||
updater.bot.send_photo(user, photo=chart, caption=f'*{item}*',
|
||||
parse_mode=ParseMode.MARKDOWN, disable_notification=True)
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ {user} - {e}')
|
||||
updater.bot.send_message(user, text=f'There was an error ⚠️\n {e}')
|
||||
if auto:
|
||||
updater.bot.send_message(user, text=f'Done ✅')
|
||||
finally:
|
||||
if user_data:
|
||||
user_data[Section.Running.value] = False
|
||||
@ -81,16 +104,9 @@ def send_updates(context: CallbackContext):
|
||||
return
|
||||
|
||||
SENDING = True
|
||||
now = current_timestamp()
|
||||
|
||||
for user, user_data in persistence.user_data.items():
|
||||
enabled = user_data.setdefault(Section.Enabled.value, True)
|
||||
last_run = user_data.setdefault(Section.LastRun.value, 0)
|
||||
frequency = parse(user_data.setdefault(Section.Frequency.value, '1d'))
|
||||
|
||||
if enabled and last_run + frequency < now:
|
||||
delta = now - user_data.setdefault(Section.Interval.value, delta_timestamp(days=365))
|
||||
send_update_to_user(user=user, delta=delta)
|
||||
if user_data.setdefault(Section.Enabled.value, False):
|
||||
send_update_to_user(user=user, auto=True)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
finally:
|
||||
|
@ -1,45 +1,232 @@
|
||||
from telegram import Update
|
||||
from telegram.ext import CallbackContext
|
||||
from telegram import Update, InlineKeyboardButton, ParseMode, InlineKeyboardMarkup, ReplyKeyboardRemove
|
||||
from telegram.ext import CallbackContext, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, Filters
|
||||
|
||||
from limited_list import LimitedList
|
||||
from utils import parse_command, config, Section
|
||||
from commands.config import Section
|
||||
from commands.other import send_update_to_user
|
||||
from limited_dict import LimitedDict
|
||||
from utils import parse_command, config
|
||||
|
||||
ALL, SINGLE, EDIT, ADD, DELETE, BACK, ENABLED, FREQUENCY, INTERVAL, DATA = map(chr, range(10))
|
||||
END = str(ConversationHandler.END)
|
||||
|
||||
|
||||
def get_watchlist(context: CallbackContext) -> LimitedList:
|
||||
return LimitedList(
|
||||
def get_watchlist(context: CallbackContext) -> LimitedDict:
|
||||
return LimitedDict(
|
||||
config[Section.Watchlist.value]['max_items'],
|
||||
context.user_data.setdefault(Section.Watchlist.value, []),
|
||||
context.user_data.setdefault(Section.Watchlist.value, {}),
|
||||
)
|
||||
|
||||
|
||||
def save_watchlist(context: CallbackContext, l: LimitedList):
|
||||
context.user_data[Section.Watchlist.value] = l.all()
|
||||
def save_watchlist(context: CallbackContext, limited_dict: LimitedDict):
|
||||
context.user_data[Section.Watchlist.value] = limited_dict.dict
|
||||
|
||||
|
||||
def watchlist_add(update: Update, context: CallbackContext):
|
||||
value, *rest = parse_command(update)
|
||||
def init(update: Update, context: CallbackContext):
|
||||
context.bot.delete_message(
|
||||
chat_id=update.message.chat_id,
|
||||
message_id=update.message.message_id,
|
||||
)
|
||||
return show_menu(update, context)
|
||||
|
||||
|
||||
def show_menu(update: Update, context: CallbackContext):
|
||||
wl = get_watchlist(context)
|
||||
wl.add(str(value).upper())
|
||||
save_watchlist(context, wl)
|
||||
update.message.reply_text('Saved 💾')
|
||||
saved = [
|
||||
[InlineKeyboardButton(item, callback_data=item)]
|
||||
for item in wl.all()
|
||||
]
|
||||
options = [[
|
||||
InlineKeyboardButton('Add', callback_data=ADD),
|
||||
InlineKeyboardButton('Done', callback_data=END)
|
||||
]]
|
||||
update.effective_user.send_message(
|
||||
'_Your Watchlist:_\n'
|
||||
f'*{len(wl)}/{wl.limit}* slots filled\n'
|
||||
'\nYou can add, modify or delete items',
|
||||
parse_mode=ParseMode.MARKDOWN,
|
||||
reply_markup=InlineKeyboardMarkup(saved + options, one_time_keyboard=True)
|
||||
)
|
||||
|
||||
return ALL
|
||||
|
||||
|
||||
def watchlist_delete(update: Update, context: CallbackContext):
|
||||
value, *rest = parse_command(update)
|
||||
def menu(update: Update, context: CallbackContext):
|
||||
context.bot.delete_message(
|
||||
chat_id=update.callback_query.message.chat_id,
|
||||
message_id=update.callback_query.message.message_id,
|
||||
)
|
||||
|
||||
selected: str = update.callback_query.data
|
||||
if selected == ADD:
|
||||
return show_add(update, context)
|
||||
elif selected == END:
|
||||
return ConversationHandler.END
|
||||
else:
|
||||
context.user_data[Section.CurrentToEdit.value] = selected
|
||||
return show_single(update, context)
|
||||
|
||||
|
||||
def show_add(update: Update, context: CallbackContext):
|
||||
update.effective_user.send_message(
|
||||
'Send me the code (e.g. AAPL or URTH)\n'
|
||||
'or send /cancel',
|
||||
reply_markup=ReplyKeyboardRemove()
|
||||
)
|
||||
return ADD
|
||||
|
||||
|
||||
def add(update, context):
|
||||
reply: str = update.message.text
|
||||
wl = get_watchlist(context)
|
||||
found = wl.delete(value)
|
||||
wl[reply.upper()] = {
|
||||
Section.Enabled.value: True,
|
||||
Section.Frequency.value: '1d',
|
||||
Section.Interval.value: '52w',
|
||||
Section.LastRun.value: 0,
|
||||
}
|
||||
save_watchlist(context, wl)
|
||||
update.message.reply_text('Deleted 🗑' if found else 'Not found ❓')
|
||||
update.message.reply_text(f'Saved {reply} 💾', reply_markup=ReplyKeyboardRemove())
|
||||
|
||||
return show_menu(update, context)
|
||||
|
||||
|
||||
def watchlist_all(update: Update, context: CallbackContext):
|
||||
items = get_watchlist(context).all()
|
||||
update.message.reply_text('\n'.join(items) if len(items) > 0 else 'Your list is empty 📭')
|
||||
def show_single(update, context):
|
||||
current = context.user_data[Section.CurrentToEdit.value]
|
||||
current_data = get_watchlist(context)[current]
|
||||
keyboard = [
|
||||
[InlineKeyboardButton(f'Turn {"off" if current_data[Section.Enabled.value] else "on"} auto updates', callback_data=ENABLED)],
|
||||
[InlineKeyboardButton(f'Frequency', callback_data=FREQUENCY)],
|
||||
[InlineKeyboardButton(f'Time interval', callback_data=INTERVAL)],
|
||||
[InlineKeyboardButton(f'Show data', callback_data=DATA)],
|
||||
[InlineKeyboardButton('Delete', callback_data=DELETE)],
|
||||
[InlineKeyboardButton('Back', callback_data=BACK)],
|
||||
]
|
||||
update.effective_user.send_message(
|
||||
'_Current settings:_\n'
|
||||
f'Auto Updates: *{current_data[Section.Enabled.value]}*\n'
|
||||
f'Frequency: *{current_data[Section.Frequency.value]}*\n'
|
||||
f'Interval: *{current_data[Section.Interval.value]}*\n'
|
||||
f'\nEdit {current}: ⬇',
|
||||
parse_mode=ParseMode.MARKDOWN,
|
||||
reply_markup=InlineKeyboardMarkup(keyboard)
|
||||
)
|
||||
|
||||
return SINGLE
|
||||
|
||||
|
||||
def watchlist_clear(update: Update, context: CallbackContext):
|
||||
def single(update, context):
|
||||
current = context.user_data[Section.CurrentToEdit.value]
|
||||
selected = update.callback_query.data
|
||||
wl = get_watchlist(context)
|
||||
wl.clear()
|
||||
save_watchlist(context, wl)
|
||||
update.message.reply_text('Cleared 🧼')
|
||||
|
||||
context.bot.delete_message(
|
||||
chat_id=update.callback_query.message.chat_id,
|
||||
message_id=update.callback_query.message.message_id,
|
||||
)
|
||||
|
||||
if selected == DELETE:
|
||||
update.effective_user.send_message(f'Deleted {current} 💪')
|
||||
del wl[current]
|
||||
save_watchlist(context, wl)
|
||||
return show_menu(update, context)
|
||||
elif selected == BACK:
|
||||
return show_menu(update, context)
|
||||
elif selected == ENABLED:
|
||||
print('Changing', wl[Section.Enabled.value])
|
||||
wl[current][Section.Enabled.value] = not wl[current][Section.Enabled.value]
|
||||
save_watchlist(context, wl)
|
||||
return show_single(update, context)
|
||||
elif selected == FREQUENCY:
|
||||
return show_single_frequency(update, context)
|
||||
elif selected == INTERVAL:
|
||||
return show_single_interval(update, context)
|
||||
elif selected == DATA:
|
||||
send_update_to_user(update.effective_user['id'], False, [current])
|
||||
return show_single(update, context)
|
||||
else:
|
||||
return cancel(update, context)
|
||||
|
||||
|
||||
def show_single_frequency(update: Update, context: CallbackContext):
|
||||
keyboard = [
|
||||
[InlineKeyboardButton('2 minutes', callback_data='2m'), InlineKeyboardButton(
|
||||
'30 minutes', callback_data='30m')],
|
||||
[InlineKeyboardButton('hour', callback_data='1h'), InlineKeyboardButton(
|
||||
'4 hours', callback_data='4h')],
|
||||
[InlineKeyboardButton('12 hours', callback_data='12h'), InlineKeyboardButton(
|
||||
'day', callback_data='1d')],
|
||||
[InlineKeyboardButton('3 days', callback_data='3d'), InlineKeyboardButton(
|
||||
'week', callback_data='1w')],
|
||||
[InlineKeyboardButton('Cancel', callback_data='cancel')],
|
||||
]
|
||||
update.effective_user.send_message(
|
||||
'Send me updates every: ⬇',
|
||||
reply_markup=InlineKeyboardMarkup(keyboard)
|
||||
)
|
||||
|
||||
return FREQUENCY
|
||||
|
||||
|
||||
def single_frequency(update: Update, context: CallbackContext):
|
||||
return save_single_attribute(update, context, Section.Frequency.value)
|
||||
|
||||
|
||||
def show_single_interval(update: Update, context: CallbackContext):
|
||||
keyboard = [
|
||||
[InlineKeyboardButton('1 day', callback_data='1d'), InlineKeyboardButton(
|
||||
'2 days', callback_data='2d')],
|
||||
[InlineKeyboardButton('1 week', callback_data='7d'), InlineKeyboardButton(
|
||||
'1 weeks', callback_data='14d')],
|
||||
[InlineKeyboardButton('1 month', callback_data='30d'), InlineKeyboardButton(
|
||||
'3 months', callback_data='90d')],
|
||||
[InlineKeyboardButton('1 year', callback_data='52w'), InlineKeyboardButton(
|
||||
'2 years', callback_data='104w')],
|
||||
[InlineKeyboardButton('Cancel', callback_data='cancel')],
|
||||
]
|
||||
update.effective_user.send_message(
|
||||
'Select the graph time span 📈:',
|
||||
reply_markup=InlineKeyboardMarkup(keyboard)
|
||||
)
|
||||
|
||||
return INTERVAL
|
||||
|
||||
|
||||
def single_interval(update: Update, context: CallbackContext):
|
||||
return save_single_attribute(update, context, Section.Interval.value)
|
||||
|
||||
|
||||
def save_single_attribute(update: Update, context: CallbackContext, key):
|
||||
current = context.user_data[Section.CurrentToEdit.value]
|
||||
selected = update.callback_query.data
|
||||
wl = get_watchlist(context)
|
||||
|
||||
context.bot.delete_message(
|
||||
chat_id=update.callback_query.message.chat_id,
|
||||
message_id=update.callback_query.message.message_id,
|
||||
)
|
||||
|
||||
if selected != 'cancel':
|
||||
update.effective_user.send_message(f'Saved {selected} 💾')
|
||||
wl[current][key] = selected
|
||||
|
||||
return show_single(update, context)
|
||||
|
||||
|
||||
def cancel(update: Update, context: CallbackContext):
|
||||
update.effective_user.send_message('Canceled', reply_markup=ReplyKeyboardRemove())
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
cancel_command_handler = CommandHandler('cancel', cancel)
|
||||
|
||||
watchlist_handler = ConversationHandler(
|
||||
entry_points=[CommandHandler('list', init)],
|
||||
states={
|
||||
ALL: [CallbackQueryHandler(menu)],
|
||||
ADD: [cancel_command_handler, MessageHandler(Filters.text, add)],
|
||||
SINGLE: [cancel_command_handler, CallbackQueryHandler(single)],
|
||||
FREQUENCY: [CallbackQueryHandler(single_frequency)],
|
||||
INTERVAL: [CallbackQueryHandler(single_interval)],
|
||||
},
|
||||
fallbacks=[cancel_command_handler],
|
||||
)
|
||||
|
16
src/generator_dict.py
Normal file
16
src/generator_dict.py
Normal file
@ -0,0 +1,16 @@
|
||||
class GeneratorDict(dict):
|
||||
def __init__(self, *args, generator=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if not generator:
|
||||
def generator(x): return x
|
||||
|
||||
self.generator = generator
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return super().__getitem__(key)
|
||||
except:
|
||||
value = self.generator(key)
|
||||
super().__setitem__(key, value)
|
||||
return value
|
@ -1,38 +1,44 @@
|
||||
import time
|
||||
import math
|
||||
from math import inf
|
||||
|
||||
|
||||
class LimitedDict:
|
||||
|
||||
def __init__(self, init: dict, limit: int):
|
||||
def __init__(self, limit: int, init: dict):
|
||||
self.dict = init
|
||||
self.limit = limit
|
||||
|
||||
def set(self, key, value):
|
||||
# Delete oldest element if there are too many
|
||||
def __len__(self):
|
||||
return len(self.dict)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# Delete the oldest if there are too many entries
|
||||
if len(self.dict) + 1 > self.limit:
|
||||
timestamp = math.inf
|
||||
who = None
|
||||
timestamp = inf
|
||||
oldest = None
|
||||
for cur, item in self.dict.items():
|
||||
if item['when'] < timestamp:
|
||||
timestamp = item['when']
|
||||
who = cur
|
||||
del self.dict[who]
|
||||
oldest = cur
|
||||
del self.dict[oldest]
|
||||
|
||||
self.dict[key] = {
|
||||
'value': value,
|
||||
'when': int(time.time())
|
||||
}
|
||||
|
||||
def get(self, key):
|
||||
def __getitem__(self, key):
|
||||
value = self.dict.get(key, None)
|
||||
return value['value'] if value else None
|
||||
|
||||
def delete(self, key):
|
||||
def __delitem__(self, key):
|
||||
self.dict.pop(key, None)
|
||||
|
||||
def clear(self):
|
||||
self.dict.clear()
|
||||
|
||||
def all(self):
|
||||
return [x['value'] for x in self.dict.values()]
|
||||
return {
|
||||
key: value['value']
|
||||
for key, value in self.dict.items()
|
||||
}
|
@ -11,10 +11,13 @@ class LimitedList:
|
||||
self.data = init if init else []
|
||||
self.limit = limit
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
def _is_index(self, i: int) -> bool:
|
||||
return False if i < 0 or i > len(self.data) - 1 else True
|
||||
|
||||
def add(self, value: str):
|
||||
def add(self, value: any):
|
||||
print(f'Before {self.data}')
|
||||
# Delete oldest element if there are too many
|
||||
if len(self.data) + 1 > self.limit:
|
||||
|
@ -1,10 +1,10 @@
|
||||
import matplotlib as mpl
|
||||
from telegram.ext import CommandHandler
|
||||
|
||||
from utils import updater, persistence
|
||||
from utils import updater
|
||||
from commands.config import config_handler
|
||||
from commands.watchlist import watchlist_add, watchlist_delete, watchlist_all, watchlist_clear
|
||||
from commands.other import start, data, send_updates
|
||||
from commands.watchlist import watchlist_handler
|
||||
from commands.other import data, send_updates, start_handler, help_handler, stop_handler, error_handler
|
||||
|
||||
|
||||
def main():
|
||||
@ -14,16 +14,16 @@ def main():
|
||||
jq = updater.job_queue
|
||||
|
||||
# Handlers
|
||||
dp.add_handler(CommandHandler('add', watchlist_add))
|
||||
dp.add_handler(CommandHandler('delete', watchlist_delete))
|
||||
dp.add_handler(CommandHandler('list', watchlist_all))
|
||||
dp.add_handler(CommandHandler('clear', watchlist_clear))
|
||||
dp.add_handler(config_handler)
|
||||
dp.add_handler(CommandHandler('start', start))
|
||||
dp.add_error_handler(error_handler)
|
||||
dp.add_handler(CommandHandler('start', start_handler))
|
||||
dp.add_handler(CommandHandler('stop', stop_handler))
|
||||
dp.add_handler(CommandHandler('help', help_handler))
|
||||
dp.add_handler(CommandHandler('data', data))
|
||||
dp.add_handler(config_handler)
|
||||
dp.add_handler(watchlist_handler)
|
||||
|
||||
# Cron jobs
|
||||
jq.run_repeating(send_updates, interval=30, first=0)
|
||||
jq.run_repeating(send_updates, interval=30, first=5)
|
||||
|
||||
# Start
|
||||
print('Started 🚀')
|
||||
|
19
src/utils.py
19
src/utils.py
@ -10,19 +10,9 @@ CONFIG_FILE = './config.yml'
|
||||
|
||||
config = load(open(CONFIG_FILE, 'r'), Loader=Loader)
|
||||
persistence = PicklePersistence(DB_FILE)
|
||||
# persistence.load_singlefile()
|
||||
updater: Updater = Updater(config['token'], use_context=True, persistence=persistence)
|
||||
|
||||
|
||||
class Section(Enum):
|
||||
Watchlist = 'watchlist'
|
||||
API_Key = 'api_key'
|
||||
Running = 'running'
|
||||
Interval = 'interval' # Time axis of the graph
|
||||
Frequency = 'frequency' # How ofter updates should be sent
|
||||
LastRun = 'last_run'
|
||||
|
||||
|
||||
def current_timestamp():
|
||||
return int(datetime.now().timestamp())
|
||||
|
||||
@ -32,11 +22,8 @@ def delta_timestamp(**kwargs):
|
||||
|
||||
|
||||
def parse_command(update: Update) -> (str, str):
|
||||
"""
|
||||
Splits the command from the rest of the message and returns the tuple
|
||||
"""
|
||||
key, value = (update.message.text.split(' ', 1)[1].split(' ', 1) + [None])[:2]
|
||||
return key, value
|
||||
|
||||
|
||||
def parse_callback(update: Update) -> str:
|
||||
selected = update.callback_query.data
|
||||
cleaned = ''.join(selected.split(':')[1:]) # Remove the pattern from the start
|
||||
return cleaned
|
||||
|
Loading…
Reference in New Issue
Block a user