Merge pull request #172 from WebEngineering2/bot

Bot correction markdown and logic mistake in updater
This commit is contained in:
NormalParameter 2022-05-10 22:45:53 +02:00 committed by GitHub
commit 40e4aab4d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 281 additions and 47 deletions

View File

@ -2,8 +2,8 @@
script for communicating with webservice to get data from database script for communicating with webservice to get data from database
""" """
__author__ = "Florian Kellermann, Linus Eickhoff" __author__ = "Florian Kellermann, Linus Eickhoff"
__date__ = "26.04.2022" __date__ = "10.05.2022"
__version__ = "1.0.1" __version__ = "1.0.2"
__license__ = "None" __license__ = "None"
#side-dependencies: none #side-dependencies: none
@ -70,6 +70,12 @@ class API_Handler:
Args: Args:
email (string): email of the user email (string): email of the user
password (string): password of the user password (string): password of the user
Returns:
token (string): new token or None if not 200
Raises:
None
""" """
payload = {'email': email, 'password': password} payload = {'email': email, 'password': password}
with r.Session() as s: with r.Session() as s:
@ -91,6 +97,9 @@ class API_Handler:
Returns: Returns:
json: json of user infos json: json of user infos
Raises:
None
""" """
if max_retries <= 0: if max_retries <= 0:
return None return None
@ -113,6 +122,9 @@ class API_Handler:
Returns: Returns:
list: list of users list: list of users
Raises:
None
""" """
if max_retries <= 0: if max_retries <= 0:
return None return None
@ -136,6 +148,9 @@ class API_Handler:
Returns: Returns:
list: list of keywords list: list of keywords
Raises:
None
""" """
if max_retries <= 0: if max_retries <= 0:
return None return None
@ -165,6 +180,9 @@ class API_Handler:
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
with r.Session() as s: with r.Session() as s:
headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)} headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)}
@ -182,6 +200,9 @@ class API_Handler:
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
with r.Session() as s: with r.Session() as s:
headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)} headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)}
@ -199,6 +220,9 @@ class API_Handler:
Returns: Returns:
list: list of shares list: list of shares
Raises:
None
""" """
if max_retries <= 0: if max_retries <= 0:
return None return None
@ -223,11 +247,14 @@ class API_Handler:
Args: Args:
user_id (int): id of the user user_id (int): id of the user
isin (string): isin of the share isin (string): identifier of the share (standard is isin)
comment (string): comment of the share comment (string): comment of the share
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
with r.Session() as s: with r.Session() as s:
headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)} headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)}
@ -240,14 +267,17 @@ class API_Handler:
Args: Args:
user_id (int): id of the user user_id (int): id of the user
symbol (string): symbol of the share isin (string): identifier of the share (standard is isin)
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
with r.Session() as s: with r.Session() as s:
headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)} headers = {'Authorization': 'Bearer ' + self.token + ":" + str(user_id)}
req = s.delete(self.db_adress + "/share", json={"isin": isin}, headers=headers) # to delete a share only the isin is needed because it is unique, shares are not transactions! req = s.delete(self.db_adress + "/share", json={"isin": str(isin)}, headers=headers) # to delete a share only the isin is needed because it is unique, shares are not transactions!
return req.status_code return req.status_code
@ -260,6 +290,9 @@ class API_Handler:
Returns: Returns:
dict: dictionary of transactions dict: dictionary of transactions
Raises:
None
""" """
if max_retries <= 0: if max_retries <= 0:
return None return None
@ -288,6 +321,9 @@ class API_Handler:
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
with r.Session() as s: with r.Session() as s:
time = time[:-3] + "Z" # remove last character and add Z to make it a valid date for db time = time[:-3] + "Z" # remove last character and add Z to make it a valid date for db
@ -306,6 +342,9 @@ class API_Handler:
Returns: Returns:
dict: dictionary of portfolio dict: dictionary of portfolio
Raises:
None
""" """
if max_retries <= 0: if max_retries <= 0:
return None return None
@ -328,6 +367,9 @@ class API_Handler:
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
if not croniter.is_valid(cron_interval): # check if cron_interval is in valid format if not croniter.is_valid(cron_interval): # check if cron_interval is in valid format
print("Error: Invalid cron format") print("Error: Invalid cron format")
@ -348,6 +390,9 @@ class API_Handler:
Returns: Returns:
int: status code int: status code
Raises:
None
""" """
with r.Session() as s: with r.Session() as s:
headers = {'Authorization': 'Bearer ' + self.token} # only bot token is needed, user is chosen by email headers = {'Authorization': 'Bearer ' + self.token} # only bot token is needed, user is chosen by email

View File

@ -3,8 +3,8 @@
script for telegram bot and its functions script for telegram bot and its functions
""" """
__author__ = "Florian Kellermann, Linus Eickhoff" __author__ = "Florian Kellermann, Linus Eickhoff"
__date__ = "26.04.2022" __date__ = "10.05.2022"
__version__ = "1.2.2" __version__ = "1.2.3"
__license__ = "None" __license__ = "None"
# side-dependencies: none # side-dependencies: none
@ -23,6 +23,7 @@ import re
import news.news_fetcher as news import news.news_fetcher as news
import shares.share_fetcher as share_fetcher import shares.share_fetcher as share_fetcher
import helper_functions as hf
import datetime as dt import datetime as dt
from telebot import types from telebot import types
@ -83,7 +84,7 @@ def send_help(message):
:rtype: none :rtype: none
""" """
bot.reply_to(message, "/id or /auth get your user id\n/update get updates on your shares.\n/setAdmin set admin rights of user (ADMIN)\n/users see all users. (ADMIN)\n/me get my user info\n/news get top article for each keyword.\n/allnews get all news (last 7 days)\n/keywords get all your keywords\n/addkeyword add a keyword\n/removekeyword remove a keyword\n/share get price of specific share\n/portfolio see own portfolio\n/newtransaction add new transaction\n/interval get update interval\n/setinterval set update interval\n_For further details see https://gruppe1.testsites.info _", parse_mode='MARKDOWN') bot.reply_to(message, "/id or /auth get your user id\n/update get updates on your shares.\n/shares get update on interesting shares\n/setAdmin set admin rights of user (ADMIN)\n/users see all users. (ADMIN)\n/me get my user info\n/news get top article for each keyword.\n/allnews get all news (last 7 days)\n/keywords get all your keywords\n/addkeyword add a keyword\n/removekeyword remove a keyword\n/transactions get all transactions\n/newtransaction create new transaction\n/share get price of specific share\n/portfolio see own portfolio\n/removeshare removes share from portfolio\n/interval get update interval\n/setinterval set update interval\n For further details see https://gruppe1.testsites.info")
@bot.message_handler(commands=['users', 'Users']) # /users -> sending all users @bot.message_handler(commands=['users', 'Users']) # /users -> sending all users
@ -225,7 +226,6 @@ def update_for_user(message):
share_symbols = [] share_symbols = []
share_amounts = [] share_amounts = []
share_courses = []
my_portfolio = p_my_handler.get_user_portfolio(p_user_id) my_portfolio = p_my_handler.get_user_portfolio(p_user_id)
@ -234,15 +234,15 @@ def update_for_user(message):
print(element["count"], element["isin"]) print(element["count"], element["isin"])
share_symbols.append(element["isin"]) share_symbols.append(element["isin"])
share_amounts.append(element["count"]) share_amounts.append(element["count"])
share_courses.append(element["current_price"])
my_user = p_my_handler.get_user(p_user_id) my_user = p_my_handler.get_user(p_user_id)
send_to_user("Hello %s this is your share update:"%str(my_user["username"]), pUser_id=p_user_id) send_to_user("Hello %s this is your share update:"%str(my_user["username"]), pUser_id=p_user_id)
if len(share_symbols) != 0: if len(share_symbols) != 0:
for i in range(len(share_symbols)): for i in range(len(share_symbols)):
my_update_message = f'Symbol: {share_symbols[i]}\nCurrent Price per Share: {share_courses[i]}\nAmount owned: {share_amounts[i]}\nTotal Investment: {float(share_courses[i]) * float(share_amounts[i])}' my_price = share_fetcher.get_share_price_no_currency(share_symbols[i])
send_to_user(my_update_message, pUser_id=p_user_id) my_update_message = f'{share_fetcher.get_share_information_markdown(share_symbols[i])}\ncount: {share_amounts[i]}\nTotal: {hf.make_markdown_proof(round(float(my_price) * float(share_amounts[i]), 2))} EUR'
bot.send_message(chat_id=p_user_id, text=my_update_message, parse_mode="MARKDOWNV2")
else: else:
send_to_user("No shares found for your account. Check https://gruppe1.testsites.info to change your settings and add shares.", pUser_id=p_user_id) send_to_user("No shares found for your account. Check https://gruppe1.testsites.info to change your settings and add shares.", pUser_id=p_user_id)
@ -280,8 +280,8 @@ def send_share_update(message):
bot.register_next_step_handler(message, send_share_price) bot.register_next_step_handler(message, send_share_price)
def send_share_price(message): def send_share_price(message):
str_share_price = share_fetcher.get_share_information(str(message.text)) str_share_price = share_fetcher.get_share_information_markdown(str(message.text))
bot.reply_to(message, str_share_price) bot.reply_to(message, str_share_price, parse_mode="MARKDOWNV2")
@bot.message_handler(commands=['allnews', 'Allnews']) # /allnews -> get all news @bot.message_handler(commands=['allnews', 'Allnews']) # /allnews -> get all news
@ -304,7 +304,7 @@ def send_all_news(message):
return return
if not keywords: # true if user is registered but does not have any keywords if not keywords: # true if user is registered but does not have any keywords
bot.send_message(chat_id=user_id, text='You have no keywords. Please add some keywords with /news') bot.send_message(chat_id=user_id, text='You have no keywords. Please add some keywords with /addkeyword')
return return
keywords_search = ' OR '.join(keywords) # concat all keywords with OR -> NewsAPI can understand OR, AND, NOT etc. keywords_search = ' OR '.join(keywords) # concat all keywords with OR -> NewsAPI can understand OR, AND, NOT etc.
@ -316,7 +316,7 @@ def send_all_news(message):
if news_list: # true if news_list is not empty if news_list: # true if news_list is not empty
for article in news_list: for article in news_list:
formatted_article = news.format_article(article) formatted_article = news.format_article(article)
bot.send_message(chat_id=user_id, text=formatted_article, parse_mode="MARKDOWN") # Markdown allows to write bold text with * etc. bot.send_message(chat_id=user_id, text=formatted_article, parse_mode="MARKDOWNV2") # Markdown allows to write bold text with * etc.
else: else:
bot.send_message(chat_id=user_id, text='No news found for your keywords.') bot.send_message(chat_id=user_id, text='No news found for your keywords.')
@ -350,11 +350,13 @@ def send_news(message):
bot.send_message(chat_id=user_id, text='News Server did not respond correctly. Try again later.') bot.send_message(chat_id=user_id, text='News Server did not respond correctly. Try again later.')
if not top_news: # true if no news found for keyword (empty list) if not top_news: # true if no news found for keyword (empty list)
bot.send_message(chat_id=user_id, text=f'No news found for keyword: *{keyword}*', parse_mode="MARKDOWN") keyword = hf.make_markdown_proof(keyword)
bot.send_message(chat_id=user_id, text=f'No news found for keyword: *{keyword}*', parse_mode="MARKDOWNV2")
else: else:
keyword = hf.make_markdown_proof(keyword)
formatted_article = news.format_article(top_news[0]) # only format and send most popular news formatted_article = news.format_article(top_news[0]) # only format and send most popular news
bot.send_message(chat_id=user_id, text=f"_keyword: {keyword}_\n\n" + formatted_article, parse_mode="MARKDOWN") bot.send_message(chat_id=user_id, text=f"_keyword: {keyword}_\n\n" + formatted_article, parse_mode="MARKDOWNV2") # do not use v2 because of bugs related t "." in links
@bot.message_handler(commands=['addkeyword', 'Addkeyword']) # /addkeyword -> add keyword to user @bot.message_handler(commands=['addkeyword', 'Addkeyword']) # /addkeyword -> add keyword to user
@ -417,15 +419,21 @@ def send_keywords(message):
""" """
user_id = int(message.from_user.id) user_id = int(message.from_user.id)
keywords = api_handler.get_user_keywords(user_id) # get keywords of user keywords = api_handler.get_user_keywords(user_id) # get keywords of user
if keywords == None: # true if user is not registered if keywords == None: # true if user is not registered
bot.send_message(chat_id=user_id, text='This didn\'t work. Make sure to connect your telegram id (/id) on https://gruppe1.testsites.info') bot.send_message(chat_id=user_id, text='This didn\'t work. Make sure to connect your telegram id (/id) on https://gruppe1.testsites.info')
return return
if not keywords: # true if user is registered but does not have any keywords if not keywords: # true if user is registered but does not have any keywords
bot.send_message(chat_id=user_id, text='No keywords set for this account. Add keywords by using /addkeyword') bot.send_message(chat_id=user_id, text='No keywords set for this account. Add keywords by using /addkeyword')
return return
else: # send keyword list else: # send keyword list
keywords_str = ', '.join(keywords) keywords_str = ', '.join(keywords)
bot.send_message(chat_id=user_id, text=f'Your keywords are: _{keywords_str}_', parse_mode="MARKDOWN") keywords_str = hf.make_markdown_proof(keywords_str)
text = f'Your keywords are: _{keywords_str}_'
bot.send_message(chat_id=user_id, text=text, parse_mode="MARKDOWNV2")
@bot.message_handler(commands=['portfolio', 'Portfolio']) @bot.message_handler(commands=['portfolio', 'Portfolio'])
@ -448,11 +456,40 @@ def send_portfolio(message):
return return
else: # send portfolio else: # send portfolio
for stock in portfolio: for stock in portfolio:
comment = str(stock["comment"]) # comment may be written name of stock, comment is made by user when adding an stock to portfolio comment = hf.make_markdown_proof(str(stock["comment"])) # comment may be written name of stock, comment is made by user when adding an stock to portfolio
count = "{:.2f}".format(float(stock["count"])) # round count to 2 decimal places count = hf.make_markdown_proof("{:.2f}".format(float(stock["count"]))) # round count to 2 decimal places
isin = str(stock["isin"]) isin = hf.make_markdown_proof(str(stock["isin"]))
worth = "{:.2f}".format(float(stock["current_price"]) * float(stock["count"])) # round current_price to 2 decimal places worth = hf.make_markdown_proof("{:.2f}".format(float(stock["current_price"]) * float(stock["count"]))) # round current_price to 2 decimal places
bot.send_message(chat_id=user_id, text=f'*{comment}*\n_{isin}_\namount: {count}\nworth: ${worth}', parse_mode="MARKDOWN") # formatted message in markdown bot.send_message(chat_id=user_id, text=f'*{comment}*\n_{isin}_\namount: {count}\nworth: ${worth}', parse_mode="MARKDOWNV2") # formatted message in markdown
@bot.message_handler(commands=['removeshare', 'Removeshare'])
def remove_share(message):
""" Remove share from portfolio
:type message: message object bot
:param message: message that was reacted to, in this case always '/removeshare'
:raises: none
:rtype: none
"""
user_id = int(message.from_user.id)
bot.send_message(chat_id=user_id, text='Type ISIN/Symbol/CompanyName of share to remove (if you are unsure do /portfolio, find your share and insert the value above amount):')
bot.register_next_step_handler(message, remove_share_step) # wait for user to send ISIN, then call remove_share_step function
def remove_share_step(message):
user_id = int(message.from_user.id)
isin = str(message.text)
status = api_handler.delete_share(int(user_id), str(isin)) # remove share from portfolio
if status == 200: # statuscode 200 means share was removed successfully without errors
bot.send_message(chat_id=user_id, text=f'Share "{isin}" removed.') # checking if share to remove is in database are handled in database, not here
else:
bot.send_message(chat_id=user_id, text=f'Failed deleting share "{isin}". (statuscode {status})\nMake sure that the share is in your portfolio and written exactly like there.')
@bot.message_handler(commands=['newtransaction', 'Newtransaction']) #tbd not working rn may be deleted in future @bot.message_handler(commands=['newtransaction', 'Newtransaction']) #tbd not working rn may be deleted in future
@ -469,10 +506,11 @@ def set_new_transaction(message):
bot.send_message(chat_id=user_id, text='Type "<name of stock>,<isin/name/symbol>,<amount>,<price_per_stock_usd>" (time of transaction will be set to now, negative amount is selling, positive is buying):') bot.send_message(chat_id=user_id, text='Type "<name of stock>,<isin/name/symbol>,<amount>,<price_per_stock_usd>" (time of transaction will be set to now, negative amount is selling, positive is buying):')
bot.register_next_step_handler(message, set_new_transaction_step) bot.register_next_step_handler(message, set_new_transaction_step)
def set_new_transaction_step(message): def set_new_transaction_step(message):
user_id = int(message.from_user.id) user_id = int(message.from_user.id)
if not re.match(r"[A-Za-z0-9]+,[A-Za-z0-9]+,(-)?[0-9]+(.[0-9]+)?,[0-9]+(.[0-9]+)?", message.text): if not re.match(r"[A-Za-z0-9 ]+,[A-Za-z0-9]+,(-)?[0-9]+(.[0-9]+)?,[0-9]+(.[0-9]+)?", message.text):
bot.send_message(chat_id=user_id, text='Invalid format \n(e.g. Apple,US0378331005,53.2,120.4).\n Try again with /newtransaction.') bot.send_message(chat_id=user_id, text='Invalid format \n(e.g. Apple,US0378331005,53.2,120.4).\n Try again with /newtransaction.')
return return
@ -514,7 +552,63 @@ def send_interval(message):
return return
formatted_interval = str(interval).replace(' ', '_') # replace spaces with underscores to add to url of crontab.guru formatted_interval = str(interval).replace(' ', '_') # replace spaces with underscores to add to url of crontab.guru
bot.send_message(chat_id=user_id, text=f'Your update interval: {interval} (https://crontab.guru/#{formatted_interval})') bot.send_message(chat_id=user_id, text=f'Your update interval: {interval} (https://crontab.guru/#{formatted_interval})')
@bot.message_handler(commands=['transactions', 'Transactions'])
def send_transactions(message):
""" send transactions for user
:type message: message object bot
:param message: message that was reacted to, in this case always '/transactions'
:raises: none
:rtype: none
"""
user_id = int(message.from_user.id)
transactions = api_handler.get_user_transactions(user_id) # get transactions of user
if transactions == None: # true if user does not exist
bot.send_message(chat_id=user_id, text='This didn\'t work. Make sure to connect your telegram id (/id) on https://gruppe1.testsites.info')
return
if not transactions: # true if user has no transactions
bot.send_message(chat_id=user_id, text='You do not have any transactions.')
return
else:
for transaction in transactions:
comment = hf.make_markdown_proof(transaction['comment']) or "\(no desc\)" # if comment is empty, make it "no desc"
isin = hf.make_markdown_proof(transaction['isin'])
amount = hf.make_markdown_proof(transaction['count'])
price = hf.make_markdown_proof(transaction['price'])
time = hf.make_markdown_proof(transaction['time'])
bot.send_message(chat_id=user_id, text=f'_{comment}_\n{isin}\namount: {amount}\nprice: {price}\ntime: {time}', parse_mode="MARKDOWNV2")
@bot.message_handler(commands=['shares', 'Shares'])
def send_shares(message):
""" send shares for user
:type message: message object bot
:param message: message that was reacted to, in this case always '/shares'
:raises: none
:rtype: none
"""
user_id = int(message.from_user.id)
shares = api_handler.get_user_shares(user_id) # get shares of user
if shares == None: # true if user does not exist
bot.send_message(chat_id=user_id, text='This didn\'t work. Make sure to connect your telegram id (/id) on https://gruppe1.testsites.info')
elif not shares: # true if user has no shares
bot.send_message(chat_id=user_id, text='You do not have any shares. Add shares on https://gruppe1.testsites.info')
else:
for element in shares:
bot.send_message(chat_id=user_id, text=share_fetcher.get_share_information_markdown(element), parse_mode="MARKDOWNV2")
@bot.message_handler(commands=['setinterval', 'Setinterval']) @bot.message_handler(commands=['setinterval', 'Setinterval'])
def set_new_interval(message): def set_new_interval(message):

View File

@ -2,7 +2,7 @@
script for regularly sending updates on shares and news based on user interval script for regularly sending updates on shares and news based on user interval
""" """
__author__ = "Florian Kellermann, Linus Eickhoff" __author__ = "Florian Kellermann, Linus Eickhoff"
__date__ = "26.04.2022" __date__ = "10.05.2022"
__version__ = "1.0.2" __version__ = "1.0.2"
__license__ = "None" __license__ = "None"
@ -14,6 +14,8 @@ from bot import bot
import sys import sys
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from api_handling.api_handler import API_Handler from api_handling.api_handler import API_Handler
import shares.share_fetcher as share_fetcher
import helper_functions as hf
''' '''
@ -72,7 +74,8 @@ def update_crontab(p_my_handler):
user_ids.append(int(element["telegram_user_id"])) user_ids.append(int(element["telegram_user_id"]))
try: try:
user_crontab.append(str(element["cron"])) user_crontab.append(str(element["cron"]))
except: continue except:
user_ids.pop()
except: continue except: continue
@ -136,29 +139,39 @@ def update_for_user(p_user_id, p_my_handler):
print(element["count"], element["isin"]) print(element["count"], element["isin"])
share_symbols.append(element["isin"]) share_symbols.append(element["isin"])
share_amounts.append(element["count"]) share_amounts.append(element["count"])
share_courses.append(element["current_price"])
my_user = p_my_handler.get_user(p_user_id) my_user = p_my_handler.get_user(p_user_id)
send_to_user("Hello %s this is your share update for today:"%str(my_user["username"]), pUser_id=p_user_id) send_to_user("Hello %s this is your share update for today:"%str(my_user["username"]), pUser_id=p_user_id)
shares = p_my_handler.get_user_shares(p_user_id)
if len(share_symbols) != 0: if len(share_symbols) != 0:
for i in range(len(share_symbols)): for i in range(len(share_symbols)):
my_update_message = f'Symbol: {share_symbols[i]}\nCurrent Price per Share: {share_courses[i]}\nAmount owned: {share_amounts[i]}\nTotal Investment: {float(share_courses[i]) * float(share_amounts[i])}' my_price = share_fetcher.get_share_price_no_currency(share_symbols[i])
send_to_user(my_update_message, pUser_id=p_user_id) my_update_message = f'{share_fetcher.get_share_information_markdown(share_symbols[i])}\ncount: {share_amounts[i]}\nTotal: {hf.make_markdown_proof(round(float(my_price) * float(share_amounts[i]), 2))} EUR'
bot.send_message(chat_id=p_user_id, text=my_update_message, parse_mode="MARKDOWNV2")
else: else:
send_to_user("No shares found for your account. Check https://gruppe1.testsites.info to change your settings and add shares.", pUser_id=p_user_id) send_to_user("No shares found for your account. Check https://gruppe1.testsites.info to change your settings and add shares.", pUser_id=p_user_id)
if len(shares)!=0: # Send updates on watchlist shares if existing
send_to_user("Your watchlist shares:", pUser_id=p_user_id)
for element in shares:
send_to_user(share_fetcher.get_share_information_markdown(element), pUser_id=p_user_id, md_mode=True)
keywords = p_my_handler.get_user_keywords(p_user_id) # get keywords as array keywords = p_my_handler.get_user_keywords(p_user_id) # get keywords as array
if(keywords): # if keywords exist and array is not empty if(keywords): # if keywords exist and array is not empty
send_to_user("If you haven't read yet: \nHere are some interesting news according to your keywords:", pUser_id=p_user_id) send_to_user("If you haven't read yet: \nHere are some interesting news according to your keywords:", pUser_id=p_user_id)
for keyword in keywords: for keyword in keywords:
news = news_fetcher.get_top_news_by_keyword(keyword)["articles"] news = news_fetcher.get_top_news_by_keyword(keyword)["articles"]
keyword = hf.make_markdown_proof(keyword)
if not news: # if empty news array if not news: # if empty news array
send_to_user(f"No news found for keyword _{keyword}_.", pUser_id=p_user_id, md_mode=True) send_to_user(f"No news found for keyword _{keyword}_.", pUser_id=p_user_id, md_mode=True)
if news == None: # if news is none elif news == None: # if news is none
send_to_user(f"Server error for keyword _{keyword}_.", pUser_id=p_user_id, md_mode=True) send_to_user(f"Server error for keyword _{keyword}_.", pUser_id=p_user_id, md_mode=True)
else: else:
news_formatted = news_fetcher.format_article(news[0]) # format for message, only use the most popular article news_formatted = news_fetcher.format_article(news[0]) # format for message, only use the most popular article
@ -183,7 +196,7 @@ def send_to_user(pText, pUser_id , md_mode = False):
:rtype: none :rtype: none
""" """
if md_mode: if md_mode:
bot.send_message(chat_id=pUser_id, text=pText, parse_mode="MARKDOWN") bot.send_message(chat_id=pUser_id, text=pText, parse_mode="MARKDOWNV2")
else: else:
bot.send_message(chat_id=pUser_id, text=pText) bot.send_message(chat_id=pUser_id, text=pText)

View File

@ -0,0 +1,68 @@
"""
script for helper functions for bot related stuff
"""
__author__ = "Florian Kellermann, Linus Eickhoff"
__date__ = "10.05.2022"
__version__ = "1.0.0"
__license__ = "None"
def contains_markdownv1_symbols(text):
""" checks if text contains markdown symbols
:type text: string
:param text: text to check
:return: true if text contains markdown symbols
:rtype: bool
"""
if text.find("_") != -1 or text.find("*") != -1 or text.find("`") != -1: # check if text contains relevant markdown symbols
return True
return False
def make_markdown_proof(text): # used to avoid errors related to markdown parsemode for telegram messaging
""" makes text markdown proof
:type text: string
:param text: text to make markdown proof
:return: markdown proof text
:rtype: string
"""
text = str(text)
text = text.replace("_", "\\_") # replace _ with \_ because \ is used as escape character in markdown, double escape is needed because \ is also a escape character in strings
text = text.replace("*", "\\*")
text = text.replace("`", "\\`")
text = text.replace("[", "\\[")
text = text.replace("]", "\\]")
text = text.replace("(", "\\(")
text = text.replace(")", "\\)")
text = text.replace("#", "\\#")
text = text.replace("+", "\\+")
text = text.replace("-", "\\-")
text = text.replace("!", "\\!")
text = text.replace(".", "\\.")
text = text.replace("?", "\\?")
text = text.replace("/", "\\/")
text = text.replace("~", "\\~")
text = text.replace("|", "\\|")
text = text.replace("<", "\\<")
text = text.replace(">", "\\>")
text = text.replace("&", "\\&")
text = text.replace("^", "\\^")
text = text.replace("$", "\\$")
text = text.replace("%", "\\%")
return text
if __name__ == '__main__':
print("this is a module for helper functions for the bot and should not be run directly")
print(make_markdown_proof("_test_"))
text = make_markdown_proof("_test_")
print(f"{text}")

View File

@ -10,6 +10,8 @@ import sys
import os import os
import requests import requests
import helper_functions as hf
from newsapi import NewsApiClient from newsapi import NewsApiClient
from dotenv import load_dotenv from dotenv import load_dotenv
@ -18,14 +20,16 @@ load_dotenv() # loads environment vars
# Init # Init
api_key = os.getenv('NEWS_API_KEY') # get API Key from .env file api_key = os.getenv('NEWS_API_KEY') # get API Key from .env file
newsapi = NewsApiClient(api_key=api_key) # news api from https://newsapi.org/ newsapi = NewsApiClient(api_key=api_key) # news api from https://newsapi.org/
try: try:
# get all available news sources (e.g BBC, New York Times, etc.) # get all available news sources (e.g BBC, New York Times, etc.)
source_json = requests.get(f"https://newsapi.org/v2/top-headlines/sources?apiKey={api_key}&language=en").json() source_json = requests.get(f"https://newsapi.org/v2/top-headlines/sources?apiKey={api_key}&language=en").json()
sources = source_json["sources"] sources = source_json["sources"]
str_sources = ",".join([source["id"] for source in sources]) str_sources = ",".join([source["id"] for source in sources])
except KeyError: except KeyError:
print("Error: Could not get sources") print("Error: Could not get sources, may be blocked because of too many requests (free newsapi is limited to 100 reqs per day)")
sys.exit(1) str_sources = str("Reuters, bbc, cnn, fox-news, google-news, hacker-news, nytimes, the-huffington-post, the-new-york-times, business-insider, bbc-news, cbc-news, ESPN, fox-sports, google-news-uk, independent, the-wall-street-journal, the-washington-times, time, usa-today")
def get_all_news_by_keyword(keyword, from_date="2000-01-01"): def get_all_news_by_keyword(keyword, from_date="2000-01-01"):
@ -68,13 +72,14 @@ def format_article(article):
Returns: Returns:
String: formatted article String: formatted article
""" """
sourcename = article["source"]["name"] sourcename = hf.make_markdown_proof(article["source"]["name"]) # make attributes markdownv2 proof
headline = article["title"] headline = hf.make_markdown_proof(article["title"])
url = article["url"] url = hf.make_markdown_proof(article["url"])
formatted_article = f"_{sourcename}_\n*{headline}*\n\n{url}" # formatting in Markdown syntax formatted_article = f"_{sourcename}_\n*{headline}*\n\n{url}" # formatting in Markdown syntax
return formatted_article return formatted_article
if __name__ == '__main__': # only execute if script is called directly -> for simple testing if __name__ == '__main__': # only execute if script is called directly -> for simple testing
print("this is a module and should not be run directly") print("this is a module and should not be run directly")

View File

@ -2,13 +2,14 @@
script for share fetching (by symbols (e.g. AAPL, TSLA etc.)) script for share fetching (by symbols (e.g. AAPL, TSLA etc.))
""" """
__author__ = "Florian Kellermann, Linus Eickhoff" __author__ = "Florian Kellermann, Linus Eickhoff"
__date__ = "15.03.2022" __date__ = "10.05.2022"
__version__ = "1.0.0" __version__ = "1.0.1"
__license__ = "None" __license__ = "None"
import investpy import investpy
import pandas import pandas
from currency_converter import CurrencyConverter from currency_converter import CurrencyConverter
import helper_functions as hf
def get_share_price(str_search_for): def get_share_price(str_search_for):
"""get stock price per share for company name or isin or symbol """get stock price per share for company name or isin or symbol
@ -55,13 +56,11 @@ def get_share_price(str_search_for):
except RuntimeError: except RuntimeError:
return "None" return "None"
def get_share_price_no_currency(str_search_for): def get_share_price_no_currency(str_search_for):
"""get stock price per share for company name or isin or symbol no currency """get stock price per share for company name or isin or symbol no currency
Args: Args:
str_search_for (string): search for this string/isin str_search_for (string): search for this string/isin
Returns: none Returns: none
""" """
try: try:
@ -96,7 +95,6 @@ def get_share_price_no_currency(str_search_for):
return str_return return str_return
def get_share_information(str_search_for): def get_share_information(str_search_for):
search_result = investpy.search_quotes(text=str_search_for, products=['stocks'], search_result = investpy.search_quotes(text=str_search_for, products=['stocks'],
countries=['germany'], n_results=1) countries=['germany'], n_results=1)
@ -105,8 +103,19 @@ def get_share_information(str_search_for):
return str_return return str_return
def get_share_information_markdown(str_search_for):
search_result = investpy.search_quotes(text=str_search_for, products=['stocks'],
countries=['germany'], n_results=1)
str_return = f'*{hf.make_markdown_proof(search_result.name)}*\n_{hf.make_markdown_proof(search_result.symbol)}_\nworth: {hf.make_markdown_proof(get_share_price(str_search_for))}'
return str_return
def get_share_information_simple(str_search_for):
search_result = investpy.search_quotes(text=str_search_for, products=['stocks'],
countries=['germany'], n_results=1)
str_return = search_result.name + "\n" +search_result.symbol + "\nworth: " + get_share_price(str_search_for)
return str_return
if __name__ == "__main__": if __name__ == "__main__":
print(get_share_price("US2515661054")) print("None")
print(get_share_price("DE0005557508"))
print(get_share_price_no_currency("US2515661054"))
print(get_share_price_no_currency("DE0005557508"))