2022-05-02 15:04:54 +00:00
"""
script for telegram bot and its functions
"""
__author__ = " Florian Kellermann, Linus Eickhoff, Florian Kaiser "
__date__ = " 02.05.2022 "
2022-05-02 15:22:50 +00:00
__version__ = " 0.0.1 "
2022-05-02 15:04:54 +00:00
__license__ = " None "
# main bot at http://t.me/guess_the_price_bot
# debug bot at http://t.me/amazondebug_bot
2022-05-02 15:07:28 +00:00
import logging
2022-05-02 15:04:54 +00:00
import os
2022-05-03 16:34:00 +00:00
import re
2022-05-02 15:04:54 +00:00
import sys
2022-05-09 16:01:59 +00:00
import datetime as dt
2022-05-09 16:34:46 +00:00
import time
2022-05-02 15:04:54 +00:00
2022-05-03 10:07:54 +00:00
import sqlalchemy
2022-05-02 15:22:50 +00:00
import telebot
from dotenv import load_dotenv
from telebot import types
2022-05-09 16:34:46 +00:00
from apscheduler . schedulers . background import BackgroundScheduler
import pandas
2022-05-02 15:22:50 +00:00
2022-05-03 10:07:54 +00:00
from db import User , session , Product
from fetcher import *
2022-05-09 14:01:03 +00:00
from db import Score
2022-05-02 15:22:50 +00:00
2022-05-03 12:28:41 +00:00
2022-05-02 15:22:50 +00:00
load_dotenv ( dotenv_path = ' .env ' ) # load environment variables
2022-05-02 15:04:54 +00:00
2022-05-02 15:46:04 +00:00
BOT_VERSION = " 0.0.1 " # version of bot
2022-05-09 16:34:46 +00:00
UPDATE_PRODUCT = " 0 1 * * * "
2022-05-02 15:04:54 +00:00
bot = telebot . TeleBot ( os . getenv ( ' BOT_API_KEY ' ) )
2022-05-02 15:22:50 +00:00
@bot.message_handler ( commands = [ ' start ' , ' Start ' ] )
2022-05-02 15:04:54 +00:00
def send_start ( message ) :
2022-05-02 16:15:04 +00:00
""" send start message to user
2022-05-02 15:04:54 +00:00
2022-05-02 16:15:04 +00:00
Args :
message ( Message ) : message from telegram user , here / start
2022-05-02 15:04:54 +00:00
"""
2022-05-03 12:28:41 +00:00
bot . send_message ( chat_id = int ( message . from_user . id ) , text = ( " Welcome to the game... \
2022-05-03 16:12:01 +00:00
\nTo start please set a name for yourself or type cancel to set generated name : " ))
2022-05-03 12:28:41 +00:00
2022-05-03 11:20:49 +00:00
bot . register_next_step_handler ( message , start_name_setter )
2022-05-03 12:28:41 +00:00
2022-05-03 11:20:49 +00:00
def start_name_setter ( message ) :
""" Set name for user and send introduction
Args :
message ( Message ) : Message to react to
"""
2022-05-03 15:52:48 +00:00
user_id = int ( message . from_user . id )
user_name = " "
2022-05-03 16:34:00 +00:00
if str ( message . text ) . lower ( ) == " cancel " : # Set user name to user
2022-05-03 16:12:01 +00:00
user_name = " User " + str ( user_id ) # generate user name, user can change it with /changename
2022-05-03 15:52:48 +00:00
2022-05-09 15:23:53 +00:00
if not re . match ( r ' ^[a-zA-Z][a-zA-Z0-9_ \ -]+$ ' , str ( message . text ) ) :
2022-05-03 16:34:00 +00:00
bot . reply_to ( message , " Name has to be alphanumeric (including underscores) and start with a letter " )
return
2022-05-06 20:26:37 +00:00
user_name = str ( message . text )
2022-05-02 15:22:50 +00:00
2022-05-03 15:52:48 +00:00
try :
user = User ( telegram_id = user_id , username = user_name , admin = False )
session . add ( user )
session . commit ( )
2022-05-03 16:34:00 +00:00
bot . reply_to ( message , f " Thank you for setting your name { user_name } \
\nType / gameinfo for information about GuessThePrice \
\nType / help for an overview of all commands " )
2022-05-03 15:52:48 +00:00
except sqlalchemy . exc . IntegrityError :
session . rollback ( )
bot . reply_to ( message , " You are already registered, change name with /changename " )
2022-05-02 15:22:50 +00:00
2022-05-02 15:07:28 +00:00
telebot . logger . setLevel ( logging . DEBUG )
2022-05-02 15:22:50 +00:00
2022-05-02 16:15:04 +00:00
@bot.message_handler ( commands = [ ' help ' , ' Help ' ] )
def send_help ( message ) :
""" send all commands to user
Args :
message ( Message ) : Message from telegram user , here / help
Returns :
None : None
Raises :
None : None
"""
2022-05-02 16:33:08 +00:00
help_message = ( " /me get my user info \n "
" /help get this help message \n "
" /gameinfo get game info \n "
" /scoreboard get scoreboard \n "
" /changename change your name \n "
" /challenge get todays challenge \n "
2022-05-09 16:34:46 +00:00
" /daily make guess for today " )
2022-05-02 16:33:08 +00:00
bot . reply_to ( message , help_message , parse_mode = ' MARKDOWN ' )
2022-05-02 16:45:32 +00:00
@bot.message_handler ( commands = [ ' gameinfo ' , ' Gameinfo ' ] )
def send_gameinfo ( message ) :
""" send game info to user
Args :
message ( Message ) : Message from telegram user , here / gameinfo
Returns :
None : None
Raises :
None : None
"""
gameinfo_message = ( " GuessThePrice is a game where you have to guess \n "
" the price of an amazon product. \n "
" Start by setting your name with /changename \n "
" You can get a new challenge every day. \n "
" You are informed when a new challenge is available. \n "
" To see the challenge type /challenge \n "
" To guess the price type /guess \n "
" At 22:00 pm the scores and answer will be shown \n " )
bot . reply_to ( message , gameinfo_message , parse_mode = ' MARKDOWN ' )
2022-05-02 16:54:41 +00:00
@bot.message_handler ( commands = [ ' me ' , ' Me ' ] )
def send_user_info ( message ) :
""" send user info to user
Args :
message ( Message ) : Message from telegram user , here / me
Returns :
None : None
Raises :
None : None
"""
user_id = message . from_user . id
2022-05-09 17:35:48 +00:00
user = session . query ( User ) . filter ( User . telegram_id == user_id ) . first ( )
scores = session . query ( Score ) . filter ( User . telegram_id == user_id ) . all ( )
today_score = session . query ( Score ) . filter ( Score . date == dt . datetime . now ( ) , Score . telegram_id == user_id ) . first ( ) # get today's score object for user
2022-05-09 16:08:31 +00:00
today_guess = " not guessed today "
if today_score is not None :
today_guess = str ( today_score . guess )
2022-05-03 16:46:06 +00:00
2022-05-03 08:20:50 +00:00
if user is not None :
user_name = user . username # tbd: get user name by id from db
2022-05-09 17:35:48 +00:00
user_score = sum ( score . score for score in scores ) # tbd: get user score by adding all scores related to userid
2022-05-09 16:08:31 +00:00
user_guess = today_guess # tbd: display if user has guessed today and how much
2022-05-03 08:20:50 +00:00
user_info = ( f " Your user info: \n "
f " User ID: { user_id } \n "
f " Username: { user_name } \n "
f " Today ' s guess: { user_guess } \n "
f " Your Score: { user_score } \n " )
else :
# User not found
2022-05-09 16:01:59 +00:00
user_info = " User does not exist. "
2022-05-02 16:54:41 +00:00
bot . reply_to ( message , user_info , parse_mode = ' MARKDOWN ' )
2022-05-09 16:16:48 +00:00
@bot.message_handler ( commands = [ ' users ' , ' Users ' ] )
def send_users ( message ) :
""" send user info to user
Args : ( Message ) : Message from telegram user , here / users
Returns :
None : None
Raises :
None : None
"""
user_id = message . from_user . id
# Check if user is admin
2022-05-09 17:35:48 +00:00
if not session . query ( User ) . filter ( User . telegram_id == user_id ) . first ( ) . admin :
2022-05-09 16:16:48 +00:00
bot . reply_to ( message , " Error: Admin rights are required to see all users. " )
return
2022-05-09 16:18:14 +00:00
users = session . query ( User ) . all ( )
2022-05-09 16:19:09 +00:00
2022-05-09 16:18:14 +00:00
if len ( users ) == 0 :
bot . reply_to ( message , " No users registered. " )
2022-05-09 16:16:48 +00:00
for user in users :
user_info = ( f " Telegram ID: { user . telegram_id } \n "
f " Username: { user . username } \n "
f " Admin: { user . admin } \n " )
bot . reply_to ( message , user_info , parse_mode = ' MARKDOWN ' )
2022-05-09 14:43:45 +00:00
@bot.message_handler ( commands = [ ' setAdmin ' , ' SetAdmin ' , ' Setadmin ' , ' setadmin ' ] )
def set_admin ( message ) :
""" set admin status of user
Args :
message ( Message ) : Message from telegram user , here / setAdmin
Returns :
None : None
Raises :
None : None
"""
user_id = message . from_user . id
2022-05-09 14:57:17 +00:00
try :
2022-05-09 17:35:48 +00:00
user = session . query ( User ) . filter ( User . telegram_id == user_id ) . first ( )
2022-05-09 14:57:17 +00:00
if not user . admin :
bot . reply_to ( message , " Error: Admin rights are required to change admin rights of users. " )
return
2022-05-09 14:43:45 +00:00
2022-05-09 14:57:17 +00:00
if user . admin :
bot . reply_to ( message , " Type the telegram_id and boolean of admin attribute like <telegram_id> <value> " )
bot . register_next_step_handler ( message , set_admin_handler )
except sqlalchemy . exc . IntegrityError :
session . rollback ( )
bot . reply_to ( message , " Something went wrong. " )
2022-05-09 14:43:45 +00:00
def set_admin_handler ( message ) :
""" set admin status of user
Args : message ( Message ) : Message from telegram user , here / setAdmin
Returns : None : None
Raises : None : None
"""
if not re . match ( r ' [0-9]* (True|False|true|false) ' , str ( message . text ) ) :
bot . reply_to ( message , " Error: Invalid format. Try again with /setAdmin " )
return
telegram_id , admin = str ( message . text ) . split ( sep = " " )
2022-05-09 17:35:48 +00:00
user = session . query ( User ) . filter ( User . telegram_id == telegram_id ) . first ( )
2022-05-09 14:43:45 +00:00
if user is None :
bot . reply_to ( message , " Error: User with entered telegram id is not registered. " )
return
2022-05-09 15:02:04 +00:00
2022-05-09 14:43:45 +00:00
try :
2022-05-09 15:02:04 +00:00
if admin in ( " True " , " true " ) :
2022-05-09 14:43:45 +00:00
user . admin = True
2022-05-09 15:02:04 +00:00
elif admin in ( " False " , " false " ) :
2022-05-09 14:43:45 +00:00
user . admin = False
session . commit ( )
bot . reply_to ( message , f " Admin rights of user { user . username } set to { user . admin } " )
except sqlalchemy . exc . IntegrityError :
session . rollback ( )
bot . reply_to ( message , " Something went wrong " )
2022-05-02 17:01:19 +00:00
@bot.message_handler ( commands = [ ' scoreboard ' , ' Scoreboard ' ] )
def send_scoreboard ( message ) :
""" send scoreboard to user
Args :
message ( Message ) : Message from telegram user , here / scoreboard
Returns :
None : None
Raises :
None : None
"""
2022-05-09 17:35:48 +00:00
alltime_board = [ ]
weekly_board = [ ]
users = session . query ( User ) . all ( )
if users is None :
bot . reply_to ( message , " No users registered. " )
return
# generate alltime scoreboard
for user in users :
telegram_id = user . telegram_id
user_scores = session . query ( Score ) . filter ( Score . telegram_id == telegram_id ) . all ( )
if user_scores is None :
continue
alltime_score = sum ( score . score for score in user_scores )
alltime_board . append ( ( user . username , alltime_score ) )
if len ( alltime_board ) == 0 :
bot . reply_to ( message , " No users have scored yet. " )
# generate weekly scoreboard
for user in users :
telegram_id = user . telegram_id
user_scores = session . query ( Score ) . filter ( Score . telegram_id == telegram_id , Score . date . date ( ) . isocalendar ( ) . week == dt . date . today ( ) . isocalendar ( ) . week ) . all ( ) # only get scores of this week
if user_scores is None :
continue
weekly_score = sum ( score . score for score in user_scores )
weekly_board . append ( ( user . username , weekly_score ) )
if len ( weekly_board ) == 0 :
bot . reply_to ( message , " No users have scored yet. " )
# sort scoreboards
alltime_board . sort ( key = lambda x : x [ 1 ] , reverse = True )
weekly_board . sort ( key = lambda x : x [ 1 ] , reverse = True )
str_alltime_board = " *Scoreboard (AllTime)*: "
str_weekly_board = " *Scoreboard (Weekly)*: "
for user in alltime_board :
str_alltime_board + = f " \n { user [ 1 ] } _( { user [ 1 ] } )_ "
if len ( alltime_board ) == 0 :
bot . reply_to ( message , str_alltime_board + " \n No users have scored yet. " )
else :
bot . reply_to ( message , str_alltime_board )
for user in weekly_board :
str_weekly_board + = f " \n { user [ 1 ] } _( { user [ 1 ] } )_ "
if len ( weekly_board ) == 0 :
bot . reply_to ( message , str_weekly_board + " \n No users have scored yet. " )
2022-05-09 16:36:44 +00:00
2022-05-09 17:35:48 +00:00
else :
bot . reply_to ( message , str_weekly_board )
2022-05-09 16:36:44 +00:00
2022-05-02 17:01:19 +00:00
@bot.message_handler ( commands = [ ' challenge ' , ' Challenge ' ] )
def send_challenge ( message ) :
""" send challenge to user
Args :
message ( Message ) : Message from telegram user , here / challenge
Returns :
None : None
Raises :
None : None
"""
bot . reply_to ( message , " Challenge not implemented yet " )
2022-05-09 16:34:46 +00:00
@bot.message_handler ( commands = [ ' changename ' , ' Changename ' ] )
def change_name ( message ) :
""" change user name
2022-05-02 17:01:19 +00:00
Args :
2022-05-09 16:34:46 +00:00
message ( Message ) : Message from telegram user , here / changename
2022-05-02 17:01:19 +00:00
Returns :
None : None
Raises :
None : None
"""
2022-05-09 16:34:46 +00:00
bot . reply_to ( message , " type new name (else type \" cancel \" ): " )
bot . register_next_step_handler ( message , change_name_setter )
@bot.message_handler ( commands = [ ' daily ' , ' Daily ' ] )
def daily_message ( message ) :
2022-05-02 17:01:19 +00:00
""" change user name
Args :
message ( Message ) : Message from telegram user , here / changename
Returns :
None : None
Raises :
None : None
"""
2022-05-09 16:34:46 +00:00
user_id = int ( message . from_user . id )
bot . send_message ( chat_id = user_id , text = " Welcome to todays challenge! \n "
" As soon as the picture loads \n "
" you will have 35 seconds to send \n "
" your price guess \n " )
time . sleep ( 2 )
bot . send_message ( chat_id = user_id , text = " Lets Go! " )
time . sleep ( 1 )
for i in range ( 3 ) :
iteration = 3 - i
bot . send_message ( chat_id = user_id , text = str ( iteration ) )
iteration - = 1
time . sleep ( 1 )
2022-05-03 16:34:00 +00:00
def change_name_setter ( message ) :
""" change user name
Args :
message ( Message ) : Message to react to
Returns :
None : None
Raises :
None : None
"""
if str ( message . text ) . lower ( ) == " cancel " : # Set user name to user
bot . reply_to ( message , " Name not changed " )
return
2022-05-09 15:23:53 +00:00
if not re . match ( r ' ^[a-zA-Z][a-zA-Z0-9_ \ -]+$ ' , str ( message . text ) ) :
2022-05-03 16:34:00 +00:00
bot . reply_to ( message , " Name has to be alphanumeric (including underscores) and start with a letter " )
return
else :
user_id = int ( message . from_user . id )
user_name = str ( message . text )
try :
2022-05-09 17:35:48 +00:00
user = session . query ( User ) . filter ( User . telegram_id == user_id ) . first ( )
2022-05-03 16:34:00 +00:00
user . username = user_name
session . commit ( )
bot . reply_to ( message , f " Your name has been changed to { user_name } " )
except sqlalchemy . exc . IntegrityError :
session . rollback ( )
bot . reply_to ( message , " Something went wrong " )
2022-05-02 17:01:19 +00:00
2022-05-03 10:07:54 +00:00
@bot.message_handler ( commands = [ ' addproduct ' , ' Addproduct ' ] )
def add_product ( message ) :
""" Add product to database
Args :
message ( Message ) : Message from telegram user , here / addproduct
Returns :
None : None
Raises :
None : None
"""
2022-05-09 14:19:32 +00:00
user_id = message . from_user . id
# Check if user is admin
2022-05-09 17:35:48 +00:00
if not session . query ( User ) . filter ( User . telegram_id == user_id ) . first ( ) . admin :
2022-05-09 14:19:32 +00:00
bot . reply_to ( message , " Error: Admin rights are required to add products " )
return
2022-05-03 10:07:54 +00:00
user_id = int ( message . from_user . id )
bot . send_message ( chat_id = user_id , text = ' Please insert the Amazon product id (i.e. B00XKZYZ2S) ' )
bot . register_next_step_handler ( message , receive_product_data ) # executes function when user sends message
def receive_product_data ( message ) :
2022-05-06 20:26:37 +00:00
""" registering new product in the db and fetching it from amazon
Args :
message ( Message ) : Message that is being reacted to . Always from add_product because of next_step_handler
"""
2022-05-03 10:07:54 +00:00
user_id = int ( message . from_user . id )
product_id = str ( message . text )
product_src = fetch_url ( ' https://www.amazon.de/dp/ ' + product_id )
title = get_title ( product_src )
image_url = get_image ( product_src , get_title ( product_src ) )
price = get_price ( product_src )
description = get_description ( product_src )
bot . send_message ( chat_id = user_id , text = title )
bot . send_message ( chat_id = user_id , text = image_url )
bot . send_message ( chat_id = user_id , text = price )
bot . send_message ( chat_id = user_id , text = description )
# markup = InlineKeyboardMarkup()
# markup.row_width = 2
# markup.add(InlineKeyboardButton("Yes", callback_data="cb_yes"),
# InlineKeyboardButton("No", callback_data="cb_no"))
#
# bot.send_message(chat_id=user_id, text="Is this the product you want to add?", reply_markup=markup)
# Insert into database
try :
product = Product (
product_id = product_id ,
title = title ,
image_link = image_url ,
price = price [ 0 ] ,
currency = price [ 1 ] ,
description = description
)
session . add ( product )
session . commit ( )
bot . send_message ( chat_id = user_id , text = ' Successfully added product to database ' )
except sqlalchemy . exc . IntegrityError :
session . rollback ( )
bot . send_message ( chat_id = user_id , text = ' Product is in database already ' )
@bot.callback_query_handler ( func = lambda call : True )
def callback_query ( call ) :
if call . data == " cb_yes " :
bot . answer_callback_query ( call . id , " Answer is Yes " )
elif call . data == " cb_no " :
bot . answer_callback_query ( call . id , " Answer is No " )
2022-05-02 16:33:08 +00:00
# inline prints for debugging
@bot.inline_handler ( lambda query : query . query == ' text ' )
2022-05-02 15:04:54 +00:00
def query_text ( inline_query ) :
2022-05-02 16:17:07 +00:00
""" inline query handler for debugging
2022-05-02 15:04:54 +00:00
2022-05-02 16:17:07 +00:00
Args :
inline_query ( InlineQuery ) : inline query from telegram user
2022-05-02 16:33:08 +00:00
2022-05-02 16:17:07 +00:00
Returns :
None : None
2022-05-02 16:33:08 +00:00
2022-05-02 16:17:07 +00:00
Raises :
None : None
2022-05-02 15:04:54 +00:00
"""
try :
2022-05-02 16:33:08 +00:00
r = types . InlineQueryResultArticle ( ' 1 ' , ' Result1 ' , types . InputTextMessageContent (
' hi ' ) ) # pylint: disable=invalid-name
r2 = types . InlineQueryResultArticle (
' 2 ' , ' Result2 ' , types . InputTextMessageContent ( ' hi ' ) ) # pylint: disable=invalid-name
2022-05-02 15:04:54 +00:00
bot . answer_inline_query ( inline_query . id , [ r , r2 ] )
2022-05-02 16:33:08 +00:00
except Exception as e : # pylint: disable=broad-except, invalid-name
2022-05-02 15:04:54 +00:00
print ( e )
2022-05-02 15:22:50 +00:00
2022-05-02 15:04:54 +00:00
def main_loop ( ) :
2022-05-09 14:01:03 +00:00
# nur zum ärgern -> sympathisch :D
2022-05-02 16:17:07 +00:00
""" main loop for bot
2022-05-02 15:04:54 +00:00
2022-05-02 16:17:07 +00:00
Args :
None : None
2022-05-02 16:33:08 +00:00
2022-05-02 16:17:07 +00:00
Returns :
None : None
2022-05-02 16:33:08 +00:00
2022-05-02 16:17:07 +00:00
Raises :
None : None
2022-05-02 15:04:54 +00:00
"""
2022-05-09 16:34:46 +00:00
product_split = UPDATE_PRODUCT . split ( " " )
my_scheduler = BackgroundScheduler ( )
my_scheduler . add_job ( get_todays_product , ' cron ' \
, day_of_week = product_split [ 4 ] \
, hour = product_split [ 1 ] \
, minute = product_split [ 0 ] \
, month = product_split [ 3 ] \
, day = product_split [ 2 ] )
2022-05-02 15:04:54 +00:00
bot . infinity_polling ( )
2022-05-09 16:34:46 +00:00
def get_todays_product ( ) :
""" Setting product for this day
"""
print ( pandas . DataFrame ( session . query ( Product . price , Product . image_link ) . all ( ) ) )
2022-05-02 15:04:54 +00:00
2022-05-02 15:22:50 +00:00
2022-05-02 15:04:54 +00:00
if __name__ == ' __main__ ' :
try :
main_loop ( )
except KeyboardInterrupt :
print ( ' \n Exiting by user request. \n ' )
2022-05-02 15:22:50 +00:00
sys . exit ( 0 )