2022-03-15 07:51:34 +00:00
"""
script for telegram bot and its functions
"""
__author__ = " Florian Kellermann, Linus Eickhoff "
__date__ = " 11.03.2022 "
2022-03-15 14:02:01 +00:00
__version__ = " 0.0.4 "
2022-03-15 07:51:34 +00:00
__license__ = " None "
# side-dependencies: none
2022-03-12 16:20:33 +00:00
# Work in Progress
2022-03-15 12:53:39 +00:00
# Api-Key: 5228016873:AAGFrh0P6brag7oD3gxXjCh5gnLLE8JMvMs /debugAPI Key: 5108535940:AAF5FpPHNV96WxGCDt8aMrGGKke1VILYib4 (https://t.me/mynewdebugbot)
2022-03-12 17:31:10 +00:00
# text bot at t.me/projektaktienbot
# API Documentation https://core.telegram.org/bots/api
# Code examples https://github.com/eternnoir/pyTelegramBotAPI#getting-started
2022-03-15 08:15:12 +00:00
2022-03-12 18:44:58 +00:00
import os
2022-03-12 17:31:10 +00:00
import telebot
2022-03-12 18:24:25 +00:00
import sys
import logging
2022-03-16 16:42:46 +00:00
import json
2022-04-17 09:57:28 +00:00
import re
2022-03-15 08:15:12 +00:00
2022-03-15 13:49:36 +00:00
import news . news_fetcher as news
2022-03-16 08:30:33 +00:00
import shares . share_fetcher as share_fetcher
2022-03-29 11:37:44 +00:00
import datetime as dt
2022-03-15 12:53:39 +00:00
2022-03-12 18:24:25 +00:00
from telebot import types
2022-03-15 12:53:39 +00:00
from dotenv import load_dotenv
2022-03-29 08:49:02 +00:00
from api_handling . api_handler import API_Handler
2022-03-28 17:39:37 +00:00
2022-03-15 12:53:39 +00:00
2022-03-29 06:49:32 +00:00
load_dotenv ( dotenv_path = ' .env ' )
2022-03-12 17:31:10 +00:00
2022-03-16 16:42:46 +00:00
bot_version = " 0.2.1 "
2022-03-13 09:49:51 +00:00
user_list = [ ]
2022-03-28 17:39:37 +00:00
#create api handler
2022-04-04 15:33:54 +00:00
api_handler = API_Handler ( " https://gruppe1.testsites.info/api " , str ( os . getenv ( " BOT_EMAIL " ) ) , str ( os . getenv ( " BOT_PASSWORD " ) ) )
print ( " Webserver Token: " + str ( api_handler . token ) )
2022-03-28 17:39:37 +00:00
2022-03-13 09:49:51 +00:00
class User : # Currently saving users in this class to test functionality -> later database
2022-03-13 12:20:39 +00:00
def __init__ ( self , p_user_id , p_user_name , p_chat_id ) :
2022-03-15 14:02:01 +00:00
""" Initialize a new user
2022-04-04 16:59:00 +00:00
: type self : User
: param self : object of the class
2022-03-15 14:02:01 +00:00
: type p_user_id : int
: param p_user_id : telegram user id
: type p_user_name : str
: param p_user_name : first name of user
: type p_chat_id : int
: param p_chat_id : telegram chat id
: raises :
: rtype :
"""
2022-03-13 09:49:51 +00:00
self . user_id = int ( p_user_id )
self . chat_id = int ( p_chat_id )
2022-03-13 12:20:39 +00:00
self . user_name = str ( p_user_name )
2022-03-13 09:49:51 +00:00
2022-03-12 18:44:58 +00:00
bot = telebot . TeleBot ( os . getenv ( ' BOT_API_KEY ' ) )
2022-03-13 09:49:51 +00:00
@bot.message_handler ( commands = [ ' start ' ] ) # /start -> saving as new user and sending welcome
def send_start ( message ) :
2022-03-15 14:02:01 +00:00
""" Description
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /start '
: raises : none
: rtype : none
"""
2022-03-13 10:34:36 +00:00
new_user = User ( int ( message . from_user . id ) , message . from_user . first_name , int ( message . chat . id ) )
2022-03-13 09:49:51 +00:00
existing_already = False
for known_user in user_list :
if known_user . user_id == new_user . user_id :
existing_already = True
if existing_already == False :
2022-03-13 10:34:36 +00:00
user_list . append ( new_user )
2022-03-13 09:49:51 +00:00
bot . reply_to ( message , " Welcome to this share bot project. Type /help to get information on what this bot can do " )
2022-03-15 12:53:39 +00:00
2022-03-13 12:35:50 +00:00
@bot.message_handler ( commands = [ ' version ' ] )
def send_version ( message ) :
2022-03-15 14:02:01 +00:00
""" Sending programm version
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /version '
: raises : none
: rtype : none
"""
2022-03-15 08:05:28 +00:00
bot . reply_to ( message , bot_version )
2022-03-12 17:31:10 +00:00
2022-03-15 12:53:39 +00:00
2022-03-13 09:49:51 +00:00
@bot.message_handler ( commands = [ ' help ' ] ) # /help -> sending all functions
2022-03-12 17:31:10 +00:00
def send_welcome ( message ) :
2022-03-15 14:02:01 +00:00
""" Send all functions
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /help '
: raises : none
: rtype : none
"""
2022-04-17 10:35:59 +00:00
bot . reply_to ( message , " /id or /auth get your user id \n /update get updates on your shares. \n /users see all users. \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 ' )
2022-03-15 12:53:39 +00:00
2022-03-13 10:34:36 +00:00
@bot.message_handler ( commands = [ ' users ' ] )
def send_all_users ( message ) :
2022-03-15 14:02:01 +00:00
""" Send all users, only possible for admins
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /users '
: raises : none
: rtype : none
"""
2022-03-13 10:34:36 +00:00
print ( ' Debug: users command ' )
2022-03-15 14:02:01 +00:00
user_id = int ( message . from_user . id )
# tbd check if user is admin
2022-03-13 10:34:36 +00:00
answer = ' Current number of users: ' + str ( len ( user_list ) )
bot . send_message ( chat_id = user_id , text = answer )
for known_user in user_list :
answer = str ( known_user . user_id ) + ' : ' + known_user . user_name
bot . send_message ( chat_id = user_id , text = answer )
2022-03-13 09:49:51 +00:00
2022-03-13 08:05:44 +00:00
2022-03-13 09:49:51 +00:00
@bot.message_handler ( commands = [ ' id ' , ' auth ' ] ) # /auth or /id -> Authentication with user_id over web tool
2022-03-13 08:05:44 +00:00
def send_id ( message ) :
2022-03-15 14:02:01 +00:00
""" Send user id for authentication with browser
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /id ' or ' /auth '
: raises : none
: rtype : none
"""
2022-04-04 16:59:00 +00:00
answer = ' Your ID/Authentication Code is: [ ' + str ( message . from_user . id ) + ' ]. Enter this code in the settings on https://gruppe1.testsites.info to get updates on your shares. '
2022-03-13 08:05:44 +00:00
bot . reply_to ( message , answer )
2022-03-12 17:31:10 +00:00
2022-03-13 09:49:51 +00:00
2022-03-29 06:31:58 +00:00
#function that sends telegram status(running or offline) as message from telegram bot to user
@bot.message_handler ( commands = [ ' status ' ] )
def send_status ( message ) :
""" Sends status to user
: type message : message object bot
: param message : message that was reacted to , if no other command handler gets called
: raises : none
: rtype : none
"""
bot . reply_to ( message , " bot is running " )
2022-03-16 08:30:33 +00:00
@bot.message_handler ( commands = [ ' update ' ] )
2022-03-13 09:49:51 +00:00
def send_update ( message ) :
2022-03-15 14:02:01 +00:00
""" Send update on shares
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /help '
: raises : none
: rtype : none
"""
2022-03-13 09:49:51 +00:00
user_id = int ( message . from_user . id )
2022-03-15 14:02:01 +00:00
2022-03-16 16:42:46 +00:00
#Can be deleted when getting from database
dirname = os . path . dirname ( __file__ )
json_path = os . path . join ( dirname , ' shares/shares_example.json ' )
2022-03-15 14:02:01 +00:00
2022-03-16 16:42:46 +00:00
with open ( json_path ) as json_file :
json_share_data = json . load ( json_file )
int_share_count = int ( json_share_data [ ' share_count ' ] )
2022-03-16 16:49:57 +00:00
str_username = str ( json_share_data [ ' user ' ] )
bot . send_message ( chat_id = user_id , text = f ' Hello { str_username } . Here is the update on your currently owned shares: ' )
2022-03-16 16:42:46 +00:00
for i in range ( int_share_count ) :
my_share = json_share_data [ ' shares ' ] [ i ]
my_share_symbol = str ( my_share [ ' symbol ' ] )
my_share_amount = float ( my_share [ ' amount_bought ' ] )
my_share_buy_price = float ( my_share [ ' price_bought ' ] )
my_share_course = float ( share_fetcher . get_share_price ( my_share_symbol ) )
my_update_message = f ' Symbol: { my_share_symbol } \n Price: { my_share_course } \n Bought for: { my_share_buy_price } \n \
2022-04-04 16:59:00 +00:00
Amount owned : { my_share_amount } \nWin / Lose : { ( my_share_amount * my_share_course ) - ( my_share_amount * my_share_buy_price ) } '
2022-03-16 16:42:46 +00:00
bot . send_message ( chat_id = user_id , text = my_update_message )
2022-03-16 08:30:33 +00:00
@bot.message_handler ( commands = [ ' share ' ] )
2022-03-16 16:42:46 +00:00
def send_share_update ( message ) :
2022-03-16 08:30:33 +00:00
""" Send price of a specific share
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /share '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
2022-03-15 14:02:01 +00:00
2022-03-13 09:49:51 +00:00
#Get Information for user with this id
2022-03-16 08:30:33 +00:00
bot . send_message ( chat_id = user_id , text = ' Send symbol of share: ' )
2022-03-16 09:02:28 +00:00
#str_share_price = shares.wait_for_share.main_loop(bot)
bot . register_next_step_handler ( message , send_share_price )
2022-03-16 08:30:33 +00:00
2022-03-16 09:02:28 +00:00
def send_share_price ( message ) :
2022-03-16 16:42:46 +00:00
str_share_price = share_fetcher . get_share_price ( str ( message . text ) )
2022-03-16 09:02:28 +00:00
bot . reply_to ( message , str_share_price )
2022-03-13 09:49:51 +00:00
2022-04-04 16:59:00 +00:00
@bot.message_handler ( commands = [ ' allnews ' ] )
def send_all_news ( message ) :
2022-03-15 14:02:01 +00:00
""" Get news for keywords of user
: type message : message object bot
2022-04-04 16:59:00 +00:00
: param message : message that was reacted to , in this case always containing ' /allnews '
2022-03-15 14:02:01 +00:00
: raises : none
: rtype : none
"""
2022-03-15 12:53:39 +00:00
user_id = int ( message . from_user . id )
2022-03-29 08:49:02 +00:00
keywords = api_handler . get_user_keywords ( user_id )
2022-04-04 16:59:00 +00:00
if keywords == None :
bot . send_message ( chat_id = user_id , text = ' This didn \' t work. Make sure too connect your telegram id (/id) on https://gruppe1.testsites.info ' )
return
if not keywords :
bot . send_message ( chat_id = user_id , text = ' You have no keywords. Please add some keywords with /news ' )
return
2022-03-29 10:20:58 +00:00
keywords_search = ' OR ' . join ( keywords )
print ( keywords_search )
2022-03-29 11:37:44 +00:00
now = dt . datetime . now ( ) . date ( )
from_date = now - dt . timedelta ( days = 7 )
from_date_formatted = dt . datetime . strftime ( from_date , ' % Y- % m- %d ' )
print ( from_date_formatted )
2022-04-04 16:59:00 +00:00
news_list = news . get_all_news_by_keyword ( keywords_search , from_date_formatted ) [ " articles " ]
2022-03-29 08:49:02 +00:00
2022-03-29 10:20:58 +00:00
if news_list :
for article in news_list :
formatted_article = news . format_article ( article )
bot . send_message ( chat_id = user_id , text = formatted_article , parse_mode = " MARKDOWN " )
else :
bot . send_message ( chat_id = user_id , text = ' No news found for your keywords. ' )
2022-04-04 16:59:00 +00:00
@bot.message_handler ( commands = [ ' news ' ] )
def send_news ( message ) :
""" Get news for keywords of user
: type message : message object bot
: param message : message that was reacted to , in this case always containing ' /news '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
keywords = api_handler . get_user_keywords ( user_id )
if keywords == None :
bot . send_message ( chat_id = user_id , text = ' This didn \' t work. Make sure too connect your telegram id (/id) on https://gruppe1.testsites.info ' )
return
if not keywords :
bot . send_message ( chat_id = user_id , text = ' You have no keywords. Please add some keywords with /addkeyword ' )
return
if keywords :
for keyword in keywords :
top_news = news . get_top_news_by_keyword ( keyword ) [ " articles " ]
if top_news == None :
bot . send_message ( chat_id = user_id , text = ' News Server did not respond correctly. Try again later. ' )
return
if not top_news :
bot . send_message ( chat_id = user_id , text = f ' No news found for keyword: * { keyword } * ' , parse_mode = " MARKDOWN " )
return
formatted_article = news . format_article ( top_news [ 0 ] )
bot . send_message ( chat_id = user_id , text = f " _keyword: { keyword } _ \n \n " + formatted_article , parse_mode = " MARKDOWN " )
2022-03-15 12:53:39 +00:00
2022-03-28 17:39:37 +00:00
@bot.message_handler ( commands = [ ' addkeyword ' ] )
def add_keyword ( message ) :
""" Add keyword to user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /addkeyword '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
bot . send_message ( chat_id = user_id , text = ' Type keyword to add: ' )
bot . register_next_step_handler ( message , store_keyword )
def store_keyword ( message ) :
user_id = int ( message . from_user . id )
2022-03-29 08:49:02 +00:00
print ( str ( user_id ) )
keyword = str ( message . text ) . lower ( )
2022-04-04 16:59:00 +00:00
status = api_handler . set_keyword ( user_id , keyword )
if status == 200 :
bot . send_message ( chat_id = user_id , text = f ' Keyword " { keyword } " added. ' )
else :
bot . send_message ( chat_id = user_id , text = f ' Keyword " { keyword } " could not be stored. (statuscode { status } ) ' )
2022-03-29 08:49:02 +00:00
2022-03-29 10:04:49 +00:00
2022-03-29 08:49:02 +00:00
@bot.message_handler ( commands = [ ' removekeyword ' ] )
def remove_keyword ( message ) :
""" Remove keyword from user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /removekeyword '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
bot . send_message ( chat_id = user_id , text = ' Type keyword to remove: ' )
bot . register_next_step_handler ( message , remove_keyword_step )
def remove_keyword_step ( message ) :
user_id = int ( message . from_user . id )
keyword = str ( message . text ) . lower ( )
2022-04-04 16:59:00 +00:00
status = api_handler . delete_keyword ( user_id , keyword )
if status == 200 :
bot . send_message ( chat_id = user_id , text = f ' Keyword " { keyword } " removed. ' )
else :
bot . send_message ( chat_id = user_id , text = f ' Failed deleting keyword " { keyword } " . (statuscode { status } ) ' )
2022-03-28 17:39:37 +00:00
2022-03-29 10:04:49 +00:00
@bot.message_handler ( commands = [ ' keywords ' ] )
def send_keywords ( message ) :
""" Send keywords of user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /keywords '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
keywords = api_handler . get_user_keywords ( user_id )
2022-04-04 16:59:00 +00:00
if keywords == None :
bot . send_message ( chat_id = user_id , text = ' This didn \' t work. Make sure too connect your telegram id (/id) on https://gruppe1.testsites.info ' )
return
if not keywords :
bot . send_message ( chat_id = user_id , text = ' No keywords set for this account. Add keywords by using /addkeyword ' )
return
else :
keywords_str = ' , ' . join ( keywords )
bot . send_message ( chat_id = user_id , text = f ' Your keywords are: _ { keywords_str } _ ' , parse_mode = " MARKDOWN " )
2022-03-29 10:04:49 +00:00
2022-04-17 10:35:59 +00:00
@bot.message_handler ( commands = [ ' portfolio ' ] ) #tbd
2022-04-17 09:57:28 +00:00
def send_portfolio ( message ) :
""" Send portfolio of user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /portfolio '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
portfolio = api_handler . get_user_portfolio ( user_id )
if portfolio == None :
bot . send_message ( chat_id = user_id , text = ' This didn \' t work. Make sure too connect your telegram id (/id) on https://gruppe1.testsites.info ' )
return
if not portfolio :
bot . send_message ( chat_id = user_id , text = ' You do not have any stocks in your portfolio. ' )
return
else :
portfolio_str = ' , ' . join ( portfolio ) # tbd: format portfolio
bot . send_message ( chat_id = user_id , text = f ' Your portfolio is: _ { portfolio_str } _ ' , parse_mode = " MARKDOWN " )
2022-04-17 10:35:59 +00:00
@bot.message_handler ( commands = [ ' newtransaction ' ] ) #tbd
2022-04-17 09:57:28 +00:00
def set_new_transaction ( message ) :
""" Set new transaction for user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /newtransaction '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
bot . send_message ( chat_id = user_id , text = ' Type " <symbol>,<amount>,<price_per_stock> " (time of transaction will be set to now): ' )
bot . register_next_step_handler ( message , set_new_transaction_step )
def set_new_transaction_step ( message ) :
user_id = int ( message . from_user . id )
if not re . match ( r " [A-Za-z0-9]+,[0-9]+[.[0-9]+]?,[0-9]+[.[0-9]+]? " , message . text ) :
bot . send_message ( chat_id = user_id , text = ' Invalid format. Try again with /newtransaction. ' )
return
transaction_data = str ( message . text ) . split ( ' , ' )
symbol = str ( transaction_data [ 0 ] )
amount = float ( transaction_data [ 1 ] )
price = float ( transaction_data [ 2 ] )
time = dt . datetime . now ( )
status = api_handler . set_transaction ( user_id , amount , price , symbol , time )
if status == 200 :
bot . send_message ( chat_id = user_id , text = ' Transaction succesfully added. ' )
else :
bot . send_message ( chat_id = user_id , text = f ' Failed adding transaction. (statuscode { status } ) ' )
2022-04-17 10:35:59 +00:00
@bot.message_handler ( commands = [ ' interval ' ] ) #tbd
2022-04-17 10:20:53 +00:00
def send_interval ( message ) :
""" send interval for user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /interval '
: raises : none
: rtype : none
"""
user_id = int ( message . from_user . id )
2022-04-17 10:35:59 +00:00
interval = api_handler . get_user ( user_id )
2022-04-17 10:20:53 +00:00
if interval == None :
bot . send_message ( chat_id = user_id , text = ' This didn \' t work. Make sure too connect your telegram id (/id) on https://gruppe1.testsites.info and set an interval with /setinterval ' )
return
else :
2022-04-17 10:35:59 +00:00
interval = str ( interval [ ' cron ' ] )
2022-04-17 10:20:53 +00:00
formatted_interval = str ( interval ) . replace ( ' ' , ' _ ' )
bot . send_message ( chat_id = user_id , text = f ' Your update interval: { interval } (https://crontab.guru/# { formatted_interval } ) ' , parse_mode = " MARKDOWN " )
@bot.message_handler ( commands = [ ' setinterval ' ] )
def set_new_interval ( message ) :
""" Set new interval for user
: type message : message object bot
: param message : message that was reacted to , in this case always ' /setinterval '
: raises : none
2022-04-17 09:57:28 +00:00
2022-04-17 10:20:53 +00:00
: rtype : none
"""
user_id = int ( message . from_user . id )
2022-04-17 10:35:59 +00:00
bot . send_message ( chat_id = user_id , text = ' Type interval in cron format: \n (https://crontab.guru/) ' )
2022-04-17 10:20:53 +00:00
bot . register_next_step_handler ( message , set_new_interval_step )
def set_new_interval_step ( message ) :
user_id = int ( message . from_user . id )
interval = str ( message . text )
status = api_handler . set_cron_interval ( user_id , interval )
if status == 200 :
bot . send_message ( chat_id = user_id , text = ' Interval succesfully set. ' )
return
2022-04-17 10:35:59 +00:00
if status == - 1 : # only -1 when interval is invalid
2022-04-17 10:20:53 +00:00
bot . send_message ( chat_id = user_id , text = ' Invalid interval. Try again with /setinterval. ' )
return
else :
bot . send_message ( chat_id = user_id , text = f ' Failed setting interval. (statuscode { status } ) ' )
2022-04-17 09:57:28 +00:00
2022-04-04 16:59:00 +00:00
@bot.message_handler ( func = lambda message : True ) # Returning that command is unknown for any other statement
2022-03-12 17:31:10 +00:00
def echo_all ( message ) :
2022-03-15 14:02:01 +00:00
""" Tell that command is not known if it is no known command
: type message : message object bot
: param message : message that was reacted to , if no other command handler gets called
: raises : none
: rtype : none
"""
2022-03-13 08:05:44 +00:00
answer = ' Do not know this command or text: ' + message . text
2022-03-12 18:44:58 +00:00
bot . reply_to ( message , answer )
2022-03-13 10:34:36 +00:00
2022-03-12 18:24:25 +00:00
telebot . logger . setLevel ( logging . DEBUG )
2022-03-13 09:49:51 +00:00
@bot.inline_handler ( lambda query : query . query == ' text ' ) # inline prints for debugging
2022-03-12 18:24:25 +00:00
def query_text ( inline_query ) :
2022-03-15 14:02:01 +00:00
""" Output in the console about current user actions and status of bot
: type inline_query :
: param inline_query :
: raises : none
: rtype : none
"""
2022-03-12 18:24:25 +00:00
try :
r = types . InlineQueryResultArticle ( ' 1 ' , ' Result1 ' , types . InputTextMessageContent ( ' hi ' ) )
r2 = types . InlineQueryResultArticle ( ' 2 ' , ' Result2 ' , types . InputTextMessageContent ( ' hi ' ) )
bot . answer_inline_query ( inline_query . id , [ r , r2 ] )
except Exception as e :
print ( e )
2022-03-12 18:44:58 +00:00
2022-03-12 18:24:25 +00:00
def main_loop ( ) :
2022-03-15 14:02:01 +00:00
2022-04-05 12:00:33 +00:00
""" Start bot
2022-03-15 14:02:01 +00:00
: raises : none
: rtype : none
"""
2022-03-12 18:24:25 +00:00
bot . infinity_polling ( )
2022-03-12 17:31:10 +00:00
2022-03-12 18:24:25 +00:00
if __name__ == ' __main__ ' :
try :
main_loop ( )
except KeyboardInterrupt :
print ( ' \n Exiting by user request. \n ' )
2022-03-13 12:35:50 +00:00
sys . exit ( 0 )